evancz/url-parser/2.0.1

NOTE: this package is not compatible with Elm 0.19.1

UrlParser

Primitives

type Parser a b

Turn URLs like /blog/42/cat-herding-techniques into nice Elm data.

string : Parser (String -> a) a

Parse a segment of the path as a String.

parsePath string location
-- /alice/  ==>  Just "alice"
-- /bob     ==>  Just "bob"
-- /42/     ==>  Just "42"
int : Parser (Int -> a) a

Parse a segment of the path as an Int.

parsePath int location
-- /alice/  ==>  Nothing
-- /bob     ==>  Nothing
-- /42/     ==>  Just 42
s : String -> Parser a a

Parse a segment of the path if it matches a given string.

s "blog"  -- can parse /blog/
          -- but not /glob/ or /42/ or anything else

Path Parses

(</>) : Parser a b -> Parser b c -> Parser a c

Parse a path with multiple segments.

parsePath (s "blog" </> int) location
-- /blog/35/  ==>  Just 35
-- /blog/42   ==>  Just 42
-- /blog/     ==>  Nothing
-- /42/       ==>  Nothing


parsePath (s "search" </> string) location
-- /search/cats/  ==>  Just "cats"
-- /search/frog   ==>  Just "frog"
-- /search/       ==>  Nothing
-- /cats/         ==>  Nothing
map : a -> Parser a b -> Parser (b -> c) c

Transform a path parser.

type alias Comment = { author : String, id : Int }


rawComment : Parser (String -> Int -> a) a
rawComment =
  s "user" </> string </> s "comments" </> int


comment : Parser (Comment -> a) a
comment =
  map Comment rawComment


parsePath comment location
-- /user/bob/comments/42  ==>  Just { author = "bob", id = 42 }
-- /user/tom/comments/35  ==>  Just { author = "tom", id = 35 }
-- /user/sam/             ==>  Nothing
oneOf : List (Parser a b) -> Parser a b

Try a bunch of different path parsers.

type Route
  = Search String
  | Blog Int
  | User String
  | Comment String Int


route : Parser (Route -> a) a
route =
  oneOf
    [ map Search  (s "search" </> string)
    , map Blog    (s "blog" </> int)
    , map User    (s "user" </> string)
    , map Comment (s "user" </> string </> "comments" </> int)
    ]


parsePath route location
-- /search/cats           ==>  Just (Search "cats")
-- /search/               ==>  Nothing


-- /blog/42               ==>  Just (Blog 42)
-- /blog/cats             ==>  Nothing


-- /user/sam/             ==>  Just (User "sam")
-- /user/bob/comments/42  ==>  Just (Comment "bob" 42)
-- /user/tom/comments/35  ==>  Just (Comment "tom" 35)
-- /user/                 ==>  Nothing
top : Parser a a

A parser that does not consume any path segments.

type BlogRoute = Overview | Post Int


blogRoute : Parser (BlogRoute -> a) a
blogRoute =
  oneOf
    [ map Overview top
    , map Post  (s "post" </> int)
    ]


parsePath (s "blog" </> blogRoute) location
-- /blog/         ==>  Just Overview
-- /blog/post/42  ==>  Just (Post 42)
custom : String -> (String -> Result String a) -> Parser (a -> b) b

Create a custom path segment parser. Here is how it is used to define the int and string parsers:

int =
  custom "NUMBER" String.toInt


string =
  custom "STRING" Ok

You 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" then
      Ok segment
    else
      Err "Does not end with .css"

Query Parameter Parsers

type QueryParser a b

Turn query parameters like ?name=tom&age=42 into nice Elm data.

(<?>) : Parser a b -> QueryParser b c -> Parser a c

Parse some query parameters.

type Route = BlogList (Maybe String) | BlogPost Int


route : Parser (Route -> a) a
route =
  oneOf
    [ map BlogList (s "blog" <?> stringParam "search")
    , map BlogPost (s "blog" </> int)
    ]


parsePath route location
-- /blog/              ==>  Just (BlogList Nothing)
-- /blog/?search=cats  ==>  Just (BlogList (Just "cats"))
-- /blog/42            ==>  Just (BlogPost 42)
stringParam : String -> QueryParser (Maybe String -> a) a

Parse a query parameter as a String.

parsePath (s "blog" <?> stringParam "search") location
-- /blog/              ==>  Just (Overview Nothing)
-- /blog/?search=cats  ==>  Just (Overview (Just "cats"))
intParam : String -> QueryParser (Maybe Int -> a) a

Parse a query parameter as an Int. Maybe you want to show paginated search results. You could have a start query parameter to say which result should appear first.

parsePath (s "results" <?> intParam "start") location
-- /results           ==>  Just Nothing
-- /results?start=10  ==>  Just (Just 10)
customParam : String -> (Maybe String -> a) -> QueryParser (a -> b) b

Create a custom query parser. You could create parsers like these:

jsonParam : String -> Decoder a -> QueryParser (Maybe a -> b) b
enumParam : String -> Dict String a -> QueryParser (Maybe a -> b) b

It may be worthwhile to have these in this library directly. If you need either one in practice, please open an issue here describing your exact scenario. We can use that data to decide if they should be added.

Run a Parser

parsePath : Parser (a -> a) a -> Location -> Maybe a

Parse based on location.pathname and location.search. This parser ignores the hash entirely.

parseHash : Parser (a -> a) a -> Location -> Maybe a

Parse based on location.hash and location.search. This parser ignores the normal path entirely.