15 , class ReadForeignFields
17 , class ReadForeignVariant
21 , class WriteForeignFields
23 , class WriteForeignVariant
28import Control.Alt ((<|>))
29import Control.Monad.Except (ExceptT(..), except, runExcept, runExceptT, withExcept)
30import Data.Array.NonEmpty (NonEmptyArray, fromArray, toArray)
31import Data.Bifunctor (lmap)
32import Data.Either (Either(..), hush, note)
33import Data.Identity (Identity(..))
34import Data.List.NonEmpty (singleton)
35import Data.Maybe (Maybe(..), maybe)
36import Data.Nullable (Nullable, toMaybe, toNullable)
37import Data.Symbol (class IsSymbol, reflectSymbol)
38import Data.Traversable (sequence, traverse)
39import Data.TraversableWithIndex (traverseWithIndex)
40import Data.Variant (Variant, inj, on)
41import Effect.Exception (message, try)
42import Effect.Uncurried as EU
43import Effect.Unsafe (unsafePerformEffect)
44import Foreign (F, Foreign, ForeignError(..), MultipleErrors, fail, isNull, isUndefined, readArray, readBoolean, readChar, readInt, readNull, readNumber, readString, tagOf, unsafeFromForeign, unsafeToForeign)
45import Foreign.Index (readProp)
46import Foreign.Object (Object)
47import Foreign.Object as Object
48import Partial.Unsafe (unsafeCrashWith)
50import Prim.RowList (class RowToList, Cons, Nil, RowList)
52import Record.Builder (Builder)
53import Record.Builder as Builder
54import Type.Prelude (Proxy(..))
56-- | An alias for the Either result of decoding
58 Either MultipleErrors a
60-- | Read a JSON string to a type `a` while returning a `MultipleErrors` if the
67readJSON = runExcept <<< (readImpl <=< parseJSON)
69-- | Read a JSON string to a type `a` using `F a`. Useful with record types.
75readJSON' = readImpl <=< parseJSON
77-- | Read a JSON string to a type `a` while returning `Nothing` if the parsing
84readJSON_ = hush <<< readJSON
86-- | Write a JSON string from a type `a`.
92writeJSON = unsafeStringify <<< writeImpl
101-- | Read a Foreign value to a type
107read = runExcept <<< readImpl
109-- | Read a value of any type as Foreign to a type
115readAsForeign = read <<< unsafeToForeign
124-- | Read a Foreign value to a type, as a Maybe of type
132foreign import _parseJSON ∷ EU.EffectFn1 String Foreign
134parseJSON ∷ String -> F Foreign
138 <<< lmap (pure <<< ForeignError <<< message)
141 <<< EU.runEffectFn1 _parseJSON
143 -- Nate Faubion: "It uses unsafePerformEffect because that’s the only way to catch exceptions and still use the builtin json decoder"
144 runPure = unsafePerformEffect
146foreign import _undefined ∷ Foreign
149undefined = _undefined
151foreign import unsafeStringify ∷ ∀ a. a -> String
153-- | A class for reading foreign values to a type
154class ReadForeign a where
155 readImpl ∷ Foreign -> F a
157instance readForeign ∷ ReadForeign Foreign where
160instance readChar ∷ ReadForeign Char where
163instance readNumber ∷ ReadForeign Number where
164 readImpl = readNumber
166instance readInt ∷ ReadForeign Int where
169instance readString ∷ ReadForeign String where
170 readImpl = readString
172instance readBoolean ∷ ReadForeign Boolean where
173 readImpl = readBoolean
175instance readArray ∷ ReadForeign a => ReadForeign (Array a) where
176 readImpl = traverseWithIndex readAtIdx <=< readArray
178 readAtIdx i f = withExcept (map (ErrorAtIndex i)) (readImpl f)
180instance readMaybe ∷ ReadForeign a => ReadForeign (Maybe a) where
181 readImpl = readNullOrUndefined readImpl
183 readNullOrUndefined _ value
184 | isNull value || isUndefined value = pure Nothing
186 readNullOrUndefined f value = Just <$> f value
188instance readNullable ∷ ReadForeign a => ReadForeign (Nullable a) where
190 withExcept (map reformat)
192 <$> traverse readImpl
195 reformat error = case error of
196 TypeMismatch inner other -> TypeMismatch ("Nullable " <> inner) other
199instance readObject ∷ ReadForeign a => ReadForeign (Object.Object a) where
200 readImpl = sequence <<< Object.mapWithKey (const readImpl) <=< readObject'
202 readObject' ∷ Foreign -> F (Object Foreign)
204 | tagOf value == "Object" = pure $ unsafeFromForeign value
205 | otherwise = fail $ TypeMismatch "Object" (tagOf value)
208 ( RowToList fields fieldList
209 , ReadForeignFields fieldList () fields
211 ReadForeign (Record fields) where
212 readImpl o = flip Builder.build {} <$> getFields fieldListP o
214 fieldListP = Proxy ∷ Proxy fieldList
216-- | A class for reading foreign values from properties
217class ReadForeignFields (xs ∷ RowList Type) (from ∷ Row Type) (to ∷ Row Type) | xs -> from to where
221 F (Builder (Record from) (Record to))
223instance readFieldsCons ∷
226 , ReadForeignFields tail from from'
227 , Row.Lacks name from'
228 , Row.Cons name ty from' to
230 ReadForeignFields (Cons name ty tail) from to where
231 getFields _ obj = (compose <$> first) `exceptTApply` rest
234 value <- withExcept' (readImpl =<< readProp name obj)
235 pure $ Builder.insert nameP value
237 rest = getFields tailP obj
239 nameP = Proxy ∷ Proxy name
241 tailP = Proxy ∷ Proxy tail
243 name = reflectSymbol nameP
245 withExcept' = withExcept <<< map $ ErrorAtProperty name
247exceptTApply ∷ ∀ a b e m. Semigroup e => Applicative m => ExceptT e m (a -> b) -> ExceptT e m a -> ExceptT e m b
249 ExceptT $ applyEither
253applyEither ∷ ∀ e a b. Semigroup e => Either e (a -> b) -> Either e a -> Either e b
254applyEither (Left e) (Right _) = Left e
256applyEither (Left e1) (Left e2) = Left (e1 <> e2)
258applyEither (Right _) (Left e) = Left e
260applyEither (Right fun) (Right a) = Right (fun a)
262instance readFieldsNil ∷
263 ReadForeignFields Nil () () where
264 getFields _ _ = pure identity
266instance readForeignVariant ∷
267 ( RowToList variants rl
268 , ReadForeignVariant rl variants
270 ReadForeign (Variant variants) where
271 readImpl o = readVariantImpl (Proxy ∷ Proxy rl) o
273class ReadForeignVariant (xs ∷ RowList Type) (row ∷ Row Type) | xs -> row where
279instance readVariantNil ∷
280 ReadForeignVariant Nil trash where
281 readVariantImpl _ _ = fail $ ForeignError "Unable to match any variant member."
283instance readVariantCons ∷
286 , Row.Cons name ty trash row
287 , ReadForeignVariant tail row
289 ReadForeignVariant (Cons name ty tail) row where
290 readVariantImpl _ o =
292 obj ∷ { type ∷ String, value ∷ Foreign } <- readImpl o
293 if obj.type == name then do
294 value ∷ ty <- readImpl obj.value
295 pure $ inj namep value
297 (fail <<< ForeignError $ "Did not match variant tag " <> name)
298 <|> readVariantImpl (Proxy ∷ Proxy tail) o
300 namep = Proxy ∷ Proxy name
302 name = reflectSymbol namep
304-- -- | A class for writing a value into JSON
305-- -- | need to do this intelligently using Foreign probably, because of null and undefined whatever
306class WriteForeign a where
307 writeImpl ∷ a -> Foreign
309instance writeForeignForeign ∷ WriteForeign Foreign where
312instance writeForeignString ∷ WriteForeign String where
313 writeImpl = unsafeToForeign
315instance writeForeignInt ∷ WriteForeign Int where
316 writeImpl = unsafeToForeign
318instance writeForeignChar ∷ WriteForeign Char where
319 writeImpl = unsafeToForeign
321instance writeForeignNumber ∷ WriteForeign Number where
322 writeImpl = unsafeToForeign
324instance writeForeignBoolean ∷ WriteForeign Boolean where
325 writeImpl = unsafeToForeign
327instance writeForeignArray ∷ WriteForeign a => WriteForeign (Array a) where
328 writeImpl xs = unsafeToForeign $ writeImpl <$> xs
330instance writeForeignMaybe ∷ WriteForeign a => WriteForeign (Maybe a) where
331 writeImpl = maybe undefined writeImpl
333instance writeForeignNullable ∷ WriteForeign a => WriteForeign (Nullable a) where
334 writeImpl = maybe (unsafeToForeign $ toNullable Nothing) writeImpl <<< toMaybe
336instance writeForeignObject ∷ WriteForeign a => WriteForeign (Object.Object a) where
337 writeImpl = unsafeToForeign <<< Object.mapWithKey (const writeImpl)
339instance recordWriteForeign ∷
341 , WriteForeignFields rl row () to
343 WriteForeign (Record row) where
344 writeImpl rec = unsafeToForeign $ Builder.build steps {}
346 rlp = Proxy ∷ Proxy rl
348 steps = writeImplFields rlp rec
350class WriteForeignFields (rl ∷ RowList Type) row (from ∷ Row Type) (to ∷ Row Type) | rl -> row from to where
351 writeImplFields ∷ ∀ g. g rl -> Record row -> Builder (Record from) (Record to)
353instance consWriteForeignFields ∷
356 , WriteForeignFields tail row from from'
357 , Row.Cons name ty whatever row
358 , Row.Lacks name from'
359 , Row.Cons name Foreign from' to
361 WriteForeignFields (Cons name ty tail) row from to where
362 writeImplFields _ rec = result
364 namep = Proxy ∷ Proxy name
366 value = writeImpl $ get namep rec
368 tailp = Proxy ∷ Proxy tail
370 rest = writeImplFields tailp rec
372 result = Builder.insert namep value <<< rest
374instance nilWriteForeignFields ∷
375 WriteForeignFields Nil row () () where
376 writeImplFields _ _ = identity
378instance writeForeignVariant ∷
380 , WriteForeignVariant rl row
382 WriteForeign (Variant row) where
383 writeImpl variant = writeVariantImpl (Proxy ∷ Proxy rl) variant
385class WriteForeignVariant (rl ∷ RowList Type) (row ∷ Row Type) | rl -> row where
386 writeVariantImpl ∷ ∀ g. g rl -> Variant row -> Foreign
388instance nilWriteForeignVariant ∷
389 WriteForeignVariant Nil () where
390 -- a PureScript-defined variant cannot reach this path, but a JavaScript FFI one could.
391 writeVariantImpl _ _ = unsafeCrashWith "Variant was not able to be writen row WriteForeign."
393instance consWriteForeignVariant ∷
396 , Row.Cons name ty subRow row
397 , WriteForeignVariant tail subRow
399 WriteForeignVariant (Cons name ty tail) row where
400 writeVariantImpl _ variant =
404 (writeVariantImpl (Proxy ∷ Proxy tail))
407 namep = Proxy ∷ Proxy name
411 { type: reflectSymbol namep
412 , value: writeImpl value
415instance readForeignNEArray ∷ ReadForeign a => ReadForeign (NonEmptyArray a) where
417 raw ∷ Array a <- readImpl f
418 except $ note (singleton $ ForeignError "Nonempty array expected, got empty array") $ fromArray raw
420instance writeForeignNEArray ∷ WriteForeign a => WriteForeign (NonEmptyArray a) where
421 writeImpl a = writeImpl <<< toArray $ a
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list