andrewMacmurray/elm-url-query-pipeline/1.0.0

NOTE: the latest version is 1.0.1

Url.Query.Pipeline

Combine elm/url Url Query param parsers using a pipeline style - think elm-json-decode-pipeline but for Url Query params:

import Url exposing (Url)
import Url.Parser as Parser exposing ((<?>), s, top)
import Url.Parser.Query as Query
import Url.Query.Pipeline as Pipeline


type Route
    = Home (Maybe MyQuery)
    | AnotherRoute


type alias MyQuery =
    { one : String
    , two : Maybe String
    , three : List Int
    }


query : Query.Parser (Maybe MyQuery)
query =
    Pipeline.succeed MyQuery
        |> Pipeline.required (Query.string "one")
        |> Pipeline.optional (Query.string "two")
        |> Pipeline.with (Query.custom "three" (List.filterMap String.toInt))


routes : Parser.Parser (Route -> a) a
routes =
    Parser.oneOf
        [ Parser.map Home (top <?> query)
        , Parser.map AnotherRoute (s "another-route")
        ]


fromString : String -> Maybe Route
fromString =
    Url.fromString >> Maybe.andThen (Parser.parse routes)

Some examples from above:

fromString "http://example/another-route" == Just AnotherRoute


fromString "http://example?one=hello&two=world&three=1&three=2"
    == Just
        (Home
            (Just
                { one = "hello"
                , two = Just "world"
                , three = [ 1, 2 ]
                }
            )
        )


-- required param "one" is missing
fromString "http://example?two=world&three=1"
    == Just (Home Nothing)

The examples below use the function parse

parse : Query.Parser (Maybe MyQuery) -> String -> Maybe MyQuery

Start a Pipeline

succeed : a -> Parser (Maybe a)

Start off a pipeline

type alias MyQuery =
    { one : String
    , two : Maybe String
    , three : Int
    }


myQuery : Query.Parser (Maybe MyQuery)
myQuery =
    Pipeline.succeed MyQuery
        |> Pipeline.required (Query.string "one")
        |> Pipeline.optional (Query.string "two")
        |> Pipeline.required (Query.int "three")

Examples:

parse myQuery "one=one&two=two&three=3"
    == Just
        { one = "one"
        , two = Just "two"
        , three = 3
        }


parse myQuery "one=one&three=3"
    == Just
        { one = "one"
        , two = Nothing
        , three = 3
        }

Build a Pipeline

required : Parser (Maybe a) -> Parser (Maybe (a -> b)) -> Parser (Maybe b)

Combine a parser that must not be Nothing, if the parser returns Nothing the whole pipeline will return Nothing

type alias MyQuery =
    { one : String
    , two : Maybe String
    }


myQuery : Query.Parser (Maybe MyQuery)
myQuery =
    Pipeline.succeed MyQuery
        |> Pipeline.required (Query.string "one")
        |> Pipeline.optional (Query.string "two")

Examples:

parse myQuery "one=one&two=two"
    == Just { one = "one", two = Just "two" }


parse myQuery "one=one"
    == Just { one = "one", two = Nothing }


-- missing required param
parse myQuery "two=two"
    == Nothing
optional : Parser (Maybe a) -> Parser (Maybe (Maybe a -> b)) -> Parser (Maybe b)

Combine a parser that returns a Maybe value

type alias MyQuery =
    { one : Maybe String
    , two : Maybe String
    }


myQuery : Query.Parser (Maybe MyQuery)
myQuery =
    Pipeline.succeed MyQuery
        |> Pipeline.optional (Query.string "one")
        |> Pipeline.optional (Query.string "two")

Examples:

parse myQuery "one=one"
    == Just { one = Just "one", two = Nothing }


parse myQuery "two=two"
    == Just { one = Nothing "one", two = Just "two" }


parse myQuery ""
    == Just { one = Nothing, two = Nothing }
with : Parser a -> Parser (Maybe (a -> b)) -> Parser (Maybe b)

Combine any parser as is: useful for Lists or Custom Types

type alias MyQuery =
    { one : List String
    , two : Fruit
    }


type Fruit
    = Apple
    | Pear


myQuery : Query.Parser (Maybe MyQuery)
myQuery =
    Pipeline.succeed MyQuery
        |> Pipeline.with (Query.custom "one" identity)
        |> Pipeline.with (Query.enum "two" fruitOptions |> Query.map (Maybe.withDefault Apple))

Examples:

parse myQuery "one=a&one=b&two=pear"
    == Just { one = [ "a", "b" ], two = Pear }


parse myQuery "two=apple"
    == Just { one = [], two = Apple }
withDefault : Parser (Maybe a) -> a -> Parser (Maybe (a -> b)) -> Parser (Maybe b)

Apply a default value for a parser containing a Maybe

type alias MyQuery =
    { one : String
    , two : Fruit
    }


type Fruit
    = Apple
    | Pear


myQuery : Query.Parser (Maybe MyQuery)
myQuery =
    Pipeline.succeed MyQuery
        |> Pipeline.required (Query.string "one")
        |> Pipeline.withDefault (Query.enum "two" fruitOptions) Apple

Examples:

parse myQuery "one=one&two=pear"
    == Just { one = "one", two = Pear }


parse myQuery "one=one"
    == Just { one = "one", two = Apple }
hardcoded : a -> Parser (Maybe (a -> b)) -> Parser (Maybe b)

Apply a hardcoded value

type alias MyQuery =
    { one : String
    , two : Int
    }


myQuery : Query.Parser (Maybe MyQuery)
myQuery =
    Pipeline.succeed MyQuery
        |> Pipeline.required (Query.string "one")
        |> Pipeline.hardcoded 42

Examples:

parse myQuery "one=one"
    == Just { one = "one", two = 42 }