In the URI spec, Tim Berners-Lee says a URL looks like this:
https://example.com:8042/over/there?name=ferret#nose
\___/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragmentThis module is primarily for parsing the path part.
Parse a segment of the path as a String.
-- /alice/ ==> Just "alice"
-- /bob ==> Just "bob"
-- /42/ ==> Just "42"
-- / ==> NothingParse a segment of the path as an Int.
-- /alice/ ==> Nothing
-- /bob ==> Nothing
-- /42/ ==> Just 42
-- / ==> NothingParse a path with multiple segments.
blog : Parser (Int -> a) a
blog =
s "blog" </> int
-- /blog/35/ ==> Just 35
-- /blog/42 ==> Just 42
-- /blog/ ==> Nothing
-- /42/ ==> Nothing
search : Parser (String -> a) a
search =
s "search" </> string
-- /search/wolf/ ==> Just "wolf"
-- /search/frog ==> Just "frog"
-- /search/ ==> Nothing
-- /wolf/ ==> NothingTransform a path parser.
type alias Comment = { user : String, id : Int }
userAndId : Parser (String -> Int -> a) a
userAndId =
s "user" </> string </> s "comment" </> int
comment : Parser (Comment -> a) a
comment =
map Comment userAndId
-- /user/bob/comment/42 ==> Just { user = "bob", id = 42 }
-- /user/tom/comment/35 ==> Just { user = "tom", id = 35 }
-- /user/sam/ ==> NothingTry a bunch of different path parsers.
type Route
= Topic String
| Blog Int
| User String
| Comment String Int
route : Parser (Route -> a) a
route =
oneOf
[ map Topic (s "topic" </> string)
, map Blog (s "blog" </> int)
, map User (s "user" </> string)
, map Comment (s "user" </> string </> s "comment" </> int)
]
-- /topic/wolf ==> Just (Topic "wolf")
-- /topic/ ==> Nothing
-- /blog/42 ==> Just (Blog 42)
-- /blog/wolf ==> Nothing
-- /user/sam/ ==> Just (User "sam")
-- /user/bob/comment/42 ==> Just (Comment "bob" 42)
-- /user/tom/comment/35 ==> Just (Comment "tom" 35)
-- /user/ ==> NothingIf there are multiple parsers that could succeed, the first one wins.
A parser that does not consume any path segments.
type Route = Overview | Post Int
blog : Parser (BlogRoute -> a) a
blog =
s "blog" </>
oneOf
[ map Overview top
, map Post (s "post" </> int)
]
-- /blog/ ==> Just Overview
-- /blog/post/42 ==> Just (Post 42)Create a custom path segment parser. Here is how it is used to define the
int parser:
int : Parser (Int -> a) a
int =
custom "NUMBER" String.toIntYou can use it to define something like “only CSS files” like this:
css : Parser (String -> a) a
css =
custom "CSS_FILE" <| \segment ->
if String.endsWith ".css" segment then
Just segment
else
NothingThe Url.Parser.Query module defines its own
Parser type. This function helps you use those
with normal parsers. For example, maybe you want to add a search feature to
your blog website:
import Url.Parser.Query as Query
type Route
= Overview (Maybe String)
| Post Int
blog : Parser (Route -> a) a
blog =
oneOf
[ map Overview (s "blog" <?> Query.string "q")
, map Post (s "blog" </> int)
]
-- /blog/ ==> Just (Overview Nothing)
-- /blog/?q=wolf ==> Just (Overview (Just "wolf"))
-- /blog/wolf ==> Nothing
-- /blog/42 ==> Just (Post 42)
-- /blog/42?q=wolf ==> Just (Post 42)
-- /blog/42/wolf ==> NothingThe Url.Parser.Query module defines its own
Parser type. This function is a helper to convert
those into normal parsers.
import Url.Parser.Query as Query
-- the following expressions are both the same!
--
-- s "blog" <?> Query.string "search"
-- s "blog" </> query (Query.string "search")This may be handy if you need query parameters but are not parsing any path segments.
Create a parser for the URL fragment, the stuff after the #. This can
be handy for handling links to DOM elements within a page. Pages like this one!
type alias Docs =
(String, Maybe String)
docs : Parser (Docs -> a) a
docs =
map Tuple.pair (string </> fragment identity)
-- /List/map ==> Nothing
-- /List/#map ==> Just ("List", Just "map")
-- /List#map ==> Just ("List", Just "map")
-- /List# ==> Just ("List", Just "")
-- /List ==> Just ("List", Nothing)
-- / ==> NothingActually run a parser! You provide some Url that
represent a valid URL. From there parse runs your parser on the path, query
parameters, and fragment.
import Url
import Url.Parser exposing (Parser, parse, int, map, oneOf, s, top)
type Route = Home | Blog Int | NotFound
route : Parser (Route -> a) a
route =
oneOf
[ map Home top
, map Blog (s "blog" </> int)
]
toRoute : String -> Route
toRoute string =
case Url.fromString string of
Nothing ->
NotFound
Just url ->
Maybe.withDefault NotFound (parse route url)
-- toRoute "/blog/42" == NotFound
-- toRoute "https://example.com/" == Home
-- toRoute "https://example.com/blog" == NotFound
-- toRoute "https://example.com/blog/42" == Blog 42
-- toRoute "https://example.com/blog/42/" == Blog 42
-- toRoute "https://example.com/blog/42#wolf" == Blog 42
-- toRoute "https://example.com/blog/42?q=wolf" == Blog 42
-- toRoute "https://example.com/settings" == NotFoundFunctions like toRoute are useful when creating single-page apps with
Browser.fullscreen. I use them in init and onNavigation to handle
the initial URL and any changes.
Turn URLs like
/blog/42/cat-herding-techniquesinto nice Elm data.