@@ -0,0 +1,135 @@
1+module Yoga.Fetch.Om.QueryDefaults
2+ ( class BuildMaybeQuery
3+ , buildMaybeQuery
4+ , class HasField
5+ , class BuildMaybeQueryField
6+ , buildMaybeQueryField
7+ , class SplitQueryRow
8+ , class SplitQueryRowRL
9+ , class BuildQueryFromPartial
10+ , buildQueryFromPartial
11+ , class BuildQueryFromPartialRL
12+ , buildQueryFromPartialRL
13+ ) where
14+
15+import Data.Maybe (Maybe(..))
16+import Data.Symbol (class IsSymbol)
17+import Prim.Boolean (True, False)
18+import Prim.Row as Row
19+import Prim.RowList (class RowToList, RowList)
20+import Prim.RowList as RL
21+import Record as Record
22+import Type.Proxy (Proxy(..))
23+
24+-- | Check whether a label exists in a RowList
25+class HasField (name :: Symbol) (rl :: RowList Type) (has :: Boolean) | name rl -> has
26+
27+instance HasField name RL.Nil False
28+instance HasField name (RL.Cons name ty tail) True
29+else instance HasField name tail has => HasField name (RL.Cons other ty tail) has
30+
31+-- | Get a Maybe-wrapped value from a record, based on whether the field exists
32+class BuildMaybeQueryField (has :: Boolean) (name :: Symbol) (ty :: Type) (provided :: Row Type) where
33+ buildMaybeQueryField :: Proxy has -> Proxy name -> Record provided -> Maybe ty
34+
35+instance (IsSymbol name, Row.Cons name ty rest provided) =>
36+ BuildMaybeQueryField True name ty provided where
37+ buildMaybeQueryField _ _ provided = Just (Record.get (Proxy :: _ name) provided)
38+
39+instance BuildMaybeQueryField False name ty provided where
40+ buildMaybeQueryField _ _ _ = Nothing
41+
42+-- | Build a full Maybe-wrapped query record from a partial provided record.
43+-- | Fields present in provided get Just, missing fields get Nothing.
44+class BuildMaybeQuery (rl :: RowList Type) (providedRL :: RowList Type) (provided :: Row Type) (queryParams :: Row Type) | rl -> queryParams where
45+ buildMaybeQuery :: Proxy rl -> Proxy providedRL -> Record provided -> Record queryParams
46+
47+instance BuildMaybeQuery RL.Nil providedRL provided () where
48+ buildMaybeQuery _ _ _ = {}
49+
50+instance
51+ ( IsSymbol name
52+ , HasField name providedRL has
53+ , BuildMaybeQueryField has name ty provided
54+ , BuildMaybeQuery tail providedRL provided tailQuery
55+ , Row.Cons name (Maybe ty) tailQuery queryParams
56+ , Row.Lacks name tailQuery
57+ ) =>
58+ BuildMaybeQuery (RL.Cons name ty tail) providedRL provided queryParams where
59+ buildMaybeQuery _ prl provided = Record.insert (Proxy :: _ name) val rest
60+ where
61+ val = buildMaybeQueryField (Proxy :: _ has) (Proxy :: _ name) provided
62+ rest = buildMaybeQuery (Proxy :: _ tail) prl provided
63+
64+-- | Split a query params row into optional raw types and required raw types.
65+-- | Maybe ty → optional with raw type ty
66+-- | ty (no Maybe) → required with raw type ty (from Required query params)
67+class SplitQueryRow (queryParams :: Row Type) (optionalRaw :: Row Type) (requiredRaw :: Row Type) | queryParams -> optionalRaw requiredRaw
68+
69+instance (RowToList queryParams rl, SplitQueryRowRL rl optionalRaw requiredRaw) => SplitQueryRow queryParams optionalRaw requiredRaw
70+
71+class SplitQueryRowRL (rl :: RowList Type) (optionalRaw :: Row Type) (requiredRaw :: Row Type) | rl -> optionalRaw requiredRaw
72+
73+instance SplitQueryRowRL RL.Nil () ()
74+
75+-- Maybe ty field → optional
76+instance
77+ ( SplitQueryRowRL tail optTail reqTail
78+ , Row.Cons name ty optTail optionalRaw
79+ , Row.Lacks name optTail
80+ ) =>
81+ SplitQueryRowRL (RL.Cons name (Maybe ty) tail) optionalRaw reqTail
82+
83+-- Non-Maybe field → required (from Required query params)
84+else instance
85+ ( SplitQueryRowRL tail optTail reqTail
86+ , Row.Cons name ty reqTail requiredRaw
87+ , Row.Lacks name reqTail
88+ ) =>
89+ SplitQueryRowRL (RL.Cons name ty tail) optTail requiredRaw
90+
91+-- | Build full queryParams from a partial record that contains path params,
92+-- | required query params, and optionally some optional query params.
93+-- | Walks the queryParams RowList:
94+-- | Maybe ty field → check if raw value provided, wrap in Just or Nothing
95+-- | bare ty field → copy from provided (required, always present)
96+class BuildQueryFromPartial (queryParams :: Row Type) (provided :: Row Type) where
97+ buildQueryFromPartial :: Record provided -> Record queryParams
98+
99+instance (RowToList queryParams rl, RowToList provided prl, BuildQueryFromPartialRL rl prl provided queryParams) => BuildQueryFromPartial queryParams provided where
100+ buildQueryFromPartial = buildQueryFromPartialRL (Proxy :: _ rl) (Proxy :: _ prl)
101+
102+class BuildQueryFromPartialRL (rl :: RowList Type) (providedRL :: RowList Type) (provided :: Row Type) (queryParams :: Row Type) | rl -> queryParams where
103+ buildQueryFromPartialRL :: Proxy rl -> Proxy providedRL -> Record provided -> Record queryParams
104+
105+instance BuildQueryFromPartialRL RL.Nil providedRL provided () where
106+ buildQueryFromPartialRL _ _ _ = {}
107+
108+-- Maybe ty: check if field is provided, wrap in Just or use Nothing
109+instance
110+ ( IsSymbol name
111+ , HasField name providedRL has
112+ , BuildMaybeQueryField has name ty provided
113+ , BuildQueryFromPartialRL tail providedRL provided tailQuery
114+ , Row.Cons name (Maybe ty) tailQuery queryParams
115+ , Row.Lacks name tailQuery
116+ ) =>
117+ BuildQueryFromPartialRL (RL.Cons name (Maybe ty) tail) providedRL provided queryParams where
118+ buildQueryFromPartialRL _ prl provided = Record.insert (Proxy :: _ name) val rest
119+ where
120+ val = buildMaybeQueryField (Proxy :: _ has) (Proxy :: _ name) provided
121+ rest = buildQueryFromPartialRL (Proxy :: _ tail) prl provided
122+
123+-- bare ty (Required): always present in provided, copy directly
124+else instance
125+ ( IsSymbol name
126+ , Row.Cons name ty rest provided
127+ , BuildQueryFromPartialRL tail providedRL provided tailQuery
128+ , Row.Cons name ty tailQuery queryParams
129+ , Row.Lacks name tailQuery
130+ ) =>
131+ BuildQueryFromPartialRL (RL.Cons name ty tail) providedRL provided queryParams where
132+ buildQueryFromPartialRL _ prl provided = Record.insert (Proxy :: _ name) val rest
133+ where
134+ val = Record.get (Proxy :: _ name) provided
135+ rest = buildQueryFromPartialRL (Proxy :: _ tail) prl provided