https://discourse.elm-lang.org/t/extracting-type-metadata-from-elm-code/6251/4
Last active
September 6, 2020 10:13
-
-
Save r-k-b/c0d060775b6db3062b2f026df44affc5 to your computer and use it in GitHub Desktop.
how can we keep route encoding/decoding in sync?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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