aux-search/src/Route.elm

143 lines
3.6 KiB
Elm

module Route exposing
( Route(..)
, SearchArgs
, SearchRoute
, fromUrl
, href
, replaceUrl
, routeToString
)
import Browser.Navigation
import Html
import Html.Attributes
import Route.SearchQuery exposing (SearchQuery)
import Url
import Url.Builder exposing (QueryParameter)
import Url.Parser exposing ((</>), (<?>))
import Url.Parser.Query
-- ROUTING
type alias SearchArgs =
{ query : Maybe SearchQuery
, channel : Maybe String
, show : Maybe String
, from : Maybe Int
, size : Maybe Int
-- TODO: embed sort type
, sort : Maybe String
}
type alias SearchRoute =
SearchArgs -> Route
searchQueryParser : Url.Url -> Url.Parser.Parser (SearchArgs -> msg) msg
searchQueryParser url =
let
rawQuery =
Route.SearchQuery.toRawQuery url
maybeQuery =
Maybe.andThen (Route.SearchQuery.searchString "query") rawQuery
in
Url.Parser.map (SearchArgs maybeQuery) <|
Url.Parser.top
<?> Url.Parser.Query.string "channel"
<?> Url.Parser.Query.string "show"
<?> Url.Parser.Query.int "from"
<?> Url.Parser.Query.int "size"
<?> Url.Parser.Query.string "sort"
searchArgsToUrl : SearchArgs -> ( List QueryParameter, Maybe ( String, Route.SearchQuery.SearchQuery ) )
searchArgsToUrl args =
( List.filterMap identity
[ Maybe.map (Url.Builder.string "channel") args.channel
, Maybe.map (Url.Builder.string "show") args.show
, Maybe.map (Url.Builder.int "from") args.from
, Maybe.map (Url.Builder.int "size") args.size
, Maybe.map (Url.Builder.string "sort") args.sort
]
, Maybe.map (Tuple.pair "query") args.query
)
type Route
= NotFound
| Home
| Packages SearchArgs
| Options SearchArgs
parser : Url.Url -> Url.Parser.Parser (Route -> msg) msg
parser url =
Url.Parser.oneOf
[ Url.Parser.map Home Url.Parser.top
, Url.Parser.map NotFound <| Url.Parser.s "not-found"
, Url.Parser.map Packages <| Url.Parser.s "packages" </> searchQueryParser url
, Url.Parser.map Options <| Url.Parser.s "options" </> searchQueryParser url
]
-- PUBLIC HELPERS
href : Route -> Html.Attribute msg
href targetRoute =
Html.Attributes.href (routeToString targetRoute)
replaceUrl : Browser.Navigation.Key -> Route -> Cmd msg
replaceUrl navKey route =
Browser.Navigation.replaceUrl navKey (routeToString route)
fromUrl : Url.Url -> Maybe Route
fromUrl url =
-- The RealWorld spec treats the fragment like a path.
-- This makes it *literally* the path, so we can proceed
-- with parsing as if it had been a normal path all along.
--{ url | path = Maybe.withDefault "" url.fragment, fragment = Nothing }
Url.Parser.parse (parser url) url
-- INTERNAL
routeToString : Route -> String
routeToString =
let
buildString ( path, query, searchQuery ) =
Route.SearchQuery.absolute path query <|
Maybe.withDefault [] <|
Maybe.map List.singleton searchQuery
in
buildString << routeToPieces
routeToPieces : Route -> ( List String, List QueryParameter, Maybe ( String, Route.SearchQuery.SearchQuery ) )
routeToPieces page =
case page of
Home ->
( [], [], Nothing )
NotFound ->
( [ "not-found" ], [], Nothing )
Packages searchArgs ->
searchArgsToUrl searchArgs
|> (\( query, raw ) -> ( [ "packages" ], query, raw ))
Options searchArgs ->
searchArgsToUrl searchArgs
|> (\( query, raw ) -> ( [ "options" ], query, raw ))