Url.Parser.Query
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 fragment
This module is for parsing the query
part.
In this library, a valid query looks like ?search=hats&page=2
where each
query parameter has the format key=value
and is separated from the next
parameter by the &
character.
Parse Query Parameters
Parse a query like ?search=hat&page=2
into nice Gren data.
Handle String
parameters.
search : Parser (Maybe String)
search =
string "search"
-- ?search=cats == Just "cats"
-- ?search=42 == Just "42"
-- ?branch=left == Nothing
-- ?search=cats&search=dogs == Nothing
Check out custom
if you need to handle multiple search
parameters for some reason.
Handle Int
parameters. Maybe you want to show paginated search results:
page : Parser (Maybe Int)
page =
int "page"
-- ?page=2 == Just 2
-- ?page=17 == Just 17
-- ?page=two == Nothing
-- ?sort=date == Nothing
-- ?page=2&page=3 == Nothing
Check out custom
if you need to handle multiple page
parameters
or something like that.
Handle enumerated parameters. Maybe you want a true-or-false parameter:
import Dict
debug : Parser (Maybe Bool)
debug =
enum "debug" (Dict.fromArray [ ("true", True), ("false", False) ])
-- ?debug=true == Just True
-- ?debug=false == Just False
-- ?debug=1 == Nothing
-- ?debug=0 == Nothing
-- ?true=true == Nothing
-- ?debug=true&debug=true == Nothing
You could add 0
and 1
to the dictionary if you want to handle those as
well. You can also use map
to say map (Maybe.withDefault False) debug
to get a parser of type Parser Bool
that swallows any errors and defaults to
False
.
Note: Parameters like ?debug
with no =
are not supported by this library.
Create a custom query parser. The string
, int
, and
enum
parsers are defined using this function. It can help you handle
anything though!
Say you are unlucky enough to need to handle ?post=2&post=7
to show a couple
posts on screen at once. You could say:
posts : Parser (Array Int)
posts =
custom "post" (Array.filterMap String.toInt)
-- ?post=2 == [2]
-- ?post=2&post=7 == [2, 7]
-- ?post=2&post=x == [2]
-- ?hats=2 == []
Mapping
Transform a parser in some way. Maybe you want your page
query parser to
default to 1
if there is any problem?
page : Parser Int
page =
map (Maybe.withDefault 1) (int "page")
Combine two parsers. A query like ?search=hats&page=2
could be parsed
with something like this:
type alias Query =
{ search : Maybe String
, page : Maybe Int
}
query : Parser Query
query =
map2 Query (string "search") (int "page")
Combine three parsers. A query like ?search=hats&page=2&sort=ascending
could be parsed with something like this:
import Dict
type alias Query =
{ search : Maybe String
, page : Maybe Int
, sort : Maybe Order
}
type Order = Ascending | Descending
query : Parser Query
query =
map3 Query (string "search") (int "page") (enum "sort" order)
order : Dict.Dict String Order
order =
Dict.fromArray
[ ( "ascending", Ascending )
, ( "descending", Descending )
]
If you need higher than eight, you can define a function like this:
apply : Parser a -> Parser (a -> b) -> Parser b
apply argParser funcParser =
map2 (<|) funcParser argParser
And then you can chain it to do as many of these as you would like:
map func (string "search")
|> apply (int "page")
|> apply (int "per-page")