APIUrl
Url
URLs: parsing, printing, resolution, and percent-encoding, following RFC 3986.
A Url is a record of its components, so it is read and updated with
ordinary field access. Internationalized host names are not supported:
hosts must be ASCII.
Component
type Component = PathSegment | QueryPart | FragmentPartSource
The part of a URL a string is encoded for, which decides what stays literal.
Constructors
PathSegment
PathSegment : ComponentSource
One path segment: unreserved characters, sub-delimiters, :, and @
stay literal, and / is encoded.
Instances:
Eq Component
Show Component
Url
type Url = { scheme : Maybe String , userinfo : Maybe String , host : Maybe String , port : Maybe Int , path : String , query : Maybe String , fragment : Maybe String }Source
A URI reference, absolute or relative. A relative one has no scheme and
possibly no host. path, query, and fragment hold their encoded form.
Fields
userinfo
userinfo : Maybe StringSource
The user information before @ in the authority. It occurs only
with a host.
host
host : Maybe StringSource
The host, present exactly when the reference has an authority
(//). A registered name is lowercased, and an IPv6 literal is held
without its brackets, as a socket dial needs it.
Instances:
Eq Url
Show Url
appendPath
appendPath : List String -> Url -> UrlSource
Appends segments to the path, encoding each one, / included.
import Url
joined : String -> Maybe String
joined text =
case Url.parse text of
Just url -> Just (Url.toString (Url.appendPath ["a b", "c/d"] url))
Nothing -> Nothing
joined "http://h/base/" == Just "http://h/base/a%20b/c%2Fd"
parse
parse : String -> Maybe UrlSource
Parses a URI reference, or answers Nothing for a character its component
does not allow (such as a space or a non-ASCII character; encode it
first), a malformed percent escape, a relative reference whose first path
segment holds a :, a malformed or non-ASCII host, and a port that is not
decimal or is above 65535.
import Url
hostOf : String -> Maybe String
hostOf text =
case Url.parse text of
Just url -> Just (Maybe.withDefault "" url.host)
Nothing -> Nothing
hostOf "HTTPS://Example.com:8080/a?b#c" == Just "example.com"
hostOf "http://[::1]/" == Just "::1"
hostOf "/docs" == Just ""
Url.parse "a b" == Nothing
Url.parse "http://h:70000/" == Nothing
percentDecode
percentDecode : String -> Maybe StringSource
Decodes percent escapes, or answers Nothing for a malformed escape or
bytes that are not valid UTF-8. + stays as it is.
import Url
Url.percentDecode "a%20b+c" == Just "a b+c"
Url.percentDecode "%zz" == Nothing
Url.percentDecode "%FF" == Nothing
percentEncode
percentEncode : Component -> String -> StringSource
Percent-encodes the UTF-8 bytes of raw that component does not keep
literally, as %XX with uppercase hex.
import Url exposing (Component(..))
Url.percentEncode PathSegment "a b/c" == "a%20b%2Fc"
Url.percentEncode QueryPart "x=1&y" == "x%3D1%26y"
Url.percentEncode PathSegment "é" == "%C3%A9"
queryParams
queryParams : Url -> List (String, String)Source
The decoded key=value pairs of the query, in order. + reads as a
space, as HTML forms write it; empty parts are skipped, a part without =
has an empty value, and a part that fails to decode is kept as written.
import Url
params : String -> List (String, String)
params text =
case Url.parse text of
Just url -> Url.queryParams url
Nothing -> []
params "/s?q=a+b&&flag&x=%41" == [("q", "a b"), ("flag", ""), ("x", "A")]
params "/s" == []
requestTarget
requestTarget : Url -> StringSource
The target an HTTP request line names: the path, or / when it is empty,
followed by ? and the query when there is one. The fragment is never
part of it.
import Url
target : String -> Maybe String
target text =
case Url.parse text of
Just url -> Just (Url.requestTarget url)
Nothing -> Nothing
target "http://h/a/b?x=1#top" == Just "/a/b?x=1"
target "http://h" == Just "/"
resolve
resolve : Url -> Url -> UrlSource
Resolves reference against base (RFC 3986 §5.2): a reference with a
scheme or host replaces the base’s, an empty path keeps the base path, a
relative path joins the base path’s directory, and dot segments are
removed. The reference’s fragment always wins.
import Url
resolved : String -> String -> Maybe String
resolved base reference =
case (Url.parse base, Url.parse reference) of
(Just b, Just r) -> Just (Url.toString (Url.resolve b r))
_ -> Nothing
resolved "http://h/a/b/c" "../d" == Just "http://h/a/d"
resolved "http://h/a/b" "?q" == Just "http://h/a/b?q"
resolved "http://h/a" "//other/x" == Just "http://other/x"
toString
toString : Url -> StringSource
The text of a URL. For anything parse accepted it is the input, except
that the scheme and host are lowercased, an empty port is dropped, and an
IPv6 host regains its brackets.
import Url
roundTrip : String -> Maybe String
roundTrip text =
case Url.parse text of
Just url -> Just (Url.toString url)
Nothing -> Nothing
roundTrip "HTTP://Example.com:/a?b#c" == Just "http://example.com/a?b#c"
roundTrip "http://[::1]:80/" == Just "http://[::1]:80/"
withQuery
withQuery : List (String, String) -> Url -> UrlSource
Appends key=value pairs to the query, encoding each key and value.
Spaces become %20, which every server accepts.
import Url
added : String -> Maybe String
added text =
case Url.parse text of
Just url -> Just (Url.toString (Url.withQuery [("q", "a b"), ("n", "1")] url))
Nothing -> Nothing
added "http://h/s?x=1" == Just "http://h/s?x=1&q=a%20b&n=1"
added "http://h/s" == Just "http://h/s?q=a%20b&n=1"
