Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
how can we keep route encoding/decoding in sync?
module RouteCodec exposing (..)
import Url exposing (Url)
import Url.Parser as UP exposing ((</>), Parser, s)
type Route
= Home
| Settings
| User String UserRoute
| Task Int TaskRoute
type UserRoute
= Summary
| Activity
type TaskRoute
= Active Bool
| Overdue
type alias Codec a b c =
{ encode : c -> String
, decode : Parser (a -> b) b
}
routeToString : Route -> String
routeToString route =
case route of
Home ->
routeCodecs.home.encode ()
Settings ->
routeCodecs.settings.encode ()
User username userRoute ->
routeCodecs.user.encode ( username, userRoute )
Task id taskRoute ->
routeCodecs.task.encode ( id, taskRoute )
routeParser : Parser (Route -> a) a
routeParser =
UP.oneOf
[ routeCodecs.home.decode
, routeCodecs.settings.decode
, routeCodecs.user.decode
, routeCodecs.task.decode
]
routeFromString : String -> Maybe Route
routeFromString string =
string
|> (++) "https://x.y/"
|> Url.fromString
|> Maybe.andThen (UP.parse routeParser)
routeCodec : Codec Route a Route
routeCodec =
{ encode = routeToString
, decode = routeParser
}
routeCodecs =
let
home : Codec Route a ()
home =
{ encode = always "home"
, decode = s "home" |> UP.map Home
}
settings : Codec Route a ()
settings =
{ encode = always "settings"
, decode = s "settings" |> UP.map Home
}
user : Codec Route a ( String, UserRoute )
user =
{ encode =
\( username, route ) ->
"user/" ++ username ++ "/" ++ userRouteCodec.encode route
, decode =
(s "user" </> UP.string </> userRouteCodec.decode)
|> UP.map User
}
task : Codec Route a ( Int, TaskRoute )
task =
{ encode =
\( taskId, route ) ->
("task/" ++ String.fromInt taskId ++ "/")
++ taskRouteCodec.encode route
, decode =
(s "task" </> UP.int </> taskRouteCodec.decode)
|> UP.map Task
}
in
{ home = home
, settings = settings
, user = user
, task = task
}
-------------------------------------------------------------- ↓ UserRoutes.elm
userRouteCodec : Codec UserRoute a UserRoute
userRouteCodec =
{ encode = userRouteToString
, decode = userRouteParser
}
userRouteToString : UserRoute -> String
userRouteToString route =
case route of
Summary ->
userRouteCodecs.summary.encode ()
Activity ->
userRouteCodecs.activity.encode ()
userRouteParser : Parser (UserRoute -> a) a
userRouteParser =
UP.oneOf
[ userRouteCodecs.summary.decode
, userRouteCodecs.activity.decode
]
userRouteCodecs =
let
summary : Codec UserRoute a ()
summary =
{ encode = always "summary"
, decode = s "summary" |> UP.map Summary
}
activity : Codec UserRoute a ()
activity =
{ encode = always "activity"
, decode = s "activity" |> UP.map Summary
}
in
{ summary = summary
, activity = activity
}
-------------------------------------------------------------- ↓ TaskRoutes.elm
taskRouteCodec : Codec TaskRoute a TaskRoute
taskRouteCodec =
{ encode = taskRouteToString
, decode = taskRouteParser
}
taskRouteToString : TaskRoute -> String
taskRouteToString route =
case route of
Active bool ->
taskRouteCodecs.active.encode bool
Overdue ->
taskRouteCodecs.overdue.encode ()
taskRouteParser : Parser (TaskRoute -> a) a
taskRouteParser =
UP.oneOf
[ taskRouteCodecs.active.decode
, taskRouteCodecs.overdue.decode
]
taskRouteCodecs =
let
active : Codec TaskRoute a Bool
active =
{ encode =
\bool ->
if bool then
"active"
else
"inactive"
, decode =
UP.oneOf
[ s "active" |> UP.map (Active True)
, s "inactive" |> UP.map (Active False)
]
}
overdue : Codec TaskRoute a ()
overdue =
{ encode = always "overdue"
, decode = s "overdue" |> UP.map Overdue
}
in
{ active = active
, overdue = overdue
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment