@@ -0,0 +1,55 @@
1+module Yoga.Fetch.Om.ReadOptional
2+ ( readOptional
3+ , class ReadPartialQuery
4+ , readPartialQuery
5+ ) where
6+
7+import Data.Maybe (Maybe(..))
8+import Data.Symbol (class IsSymbol, reflectSymbol)
9+import Prim.Row as Row
10+import Prim.RowList (class RowToList, RowList)
11+import Prim.RowList as RL
12+import Record as Record
13+import Type.Proxy (Proxy(..))
14+
15+foreign import readOptionalImpl :: forall a r. String -> Record r -> Maybe a
16+
17+readOptional :: forall @name ty r. IsSymbol name => Record r -> Maybe ty
18+readOptional rec = readOptionalImpl (reflectSymbol (Proxy :: _ name)) rec
19+
20+-- | Read query params from a potentially-partial record.
21+-- | Walks the query params RowList:
22+-- | Maybe ty → read with undefined-awareness (present → Just, absent → Nothing)
23+-- | bare ty → read directly (required, always present)
24+class ReadPartialQuery (rl :: RowList Type) (queryParams :: Row Type) | rl -> queryParams where
25+ readPartialQuery :: forall r. Record r -> Record queryParams
26+
27+instance ReadPartialQuery RL.Nil () where
28+ readPartialQuery _ = {}
29+
30+-- Maybe ty: optional field, may be undefined at runtime
31+instance
32+ ( IsSymbol name
33+ , ReadPartialQuery tail tailQuery
34+ , Row.Cons name (Maybe ty) tailQuery queryParams
35+ , Row.Lacks name tailQuery
36+ ) =>
37+ ReadPartialQuery (RL.Cons name (Maybe ty) tail) queryParams where
38+ readPartialQuery rec = Record.insert (Proxy :: _ name) (readOptional @name rec) rest
39+ where
40+ rest = readPartialQuery @tail rec
41+
42+-- bare ty: required field (from Required query params), always present
43+else instance
44+ ( IsSymbol name
45+ , ReadPartialQuery tail tailQuery
46+ , Row.Cons name ty tailQuery queryParams
47+ , Row.Lacks name tailQuery
48+ ) =>
49+ ReadPartialQuery (RL.Cons name ty tail) queryParams where
50+ readPartialQuery rec = Record.insert (Proxy :: _ name) val rest
51+ where
52+ val = readRequiredField (reflectSymbol (Proxy :: _ name)) rec
53+ rest = readPartialQuery @tail rec
54+
55+foreign import readRequiredField :: forall a r. String -> Record r -> a