git.iko.soy/rowtype-yoga/purescript-yoga-json
on main help sign in
Instances for `Yoga.Tree` and `DateTime`
f213e33Mark Eibes
packages.dhall+2−2
@@ -1,5 +1,5 @@
1let upstream =
1let upstream =
2− https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20221017/packages.dhall
2+ https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20221116/packages.dhall
3− sha256:88a9c22385f181da79caa7922502558b37a5fb759d45b45e904b105b8f88f6eb
3+ sha256:a0b88cbba414b046d9e7660fb77ed433d238d4c6ab20e75a479c9c6c823f2812
4
4
5in upstream
5in upstream
spago.dhall+2
@@ -12,6 +12,7 @@
12 , "foldable-traversable"
12 , "foldable-traversable"
13 , "foreign"
13 , "foreign"
14 , "foreign-object"
14 , "foreign-object"
15+ , "free"
15 , "identity"
16 , "identity"
16 , "integers"
17 , "integers"
17 , "js-bigints"
18 , "js-bigints"
@@ -31,6 +32,7 @@
31 , "typelevel-prelude"
32 , "typelevel-prelude"
32 , "unsafe-coerce"
33 , "unsafe-coerce"
33 , "variant"
34 , "variant"
35+ , "yoga-tree"
34 ]
36 ]
35, packages = ./packages.dhall
37, packages = ./packages.dhall
36, sources = [ "src/**/*.purs" ]
38, sources = [ "src/**/*.purs" ]
src/Yoga/JSON.purs+36−4
@@ -11,7 +11,6 @@ module Yoga.JSON
11 , parseJSON
11 , parseJSON
12 , undefined
12 , undefined
13 , unsafeStringify
13 , unsafeStringify
14−
15 , class ReadForeign
14 , class ReadForeign
16 , readImpl
15 , readImpl
17 , class ReadForeignFields
16 , class ReadForeignFields
@@ -21,25 +20,25 @@ module Yoga.JSON
21 , class ReadTuple
20 , class ReadTuple
22 , readTupleImpl
21 , readTupleImpl
23 , tupleSize
22 , tupleSize
24−
25 , class WriteForeign
23 , class WriteForeign
26 , writeImpl
24 , writeImpl
27 , class WriteForeignFields
25 , class WriteForeignFields
28 , writeImplFields
26 , writeImplFields
29 , class WriteForeignVariant
27 , class WriteForeignVariant
30 , writeVariantImpl
28 , writeVariantImpl
31−
32 ) where
29 ) where
33
30
34import Prelude
31import Prelude
35
32
36import Control.Alt ((<|>))
33import Control.Alt ((<|>))
37import Control.Apply (lift2)
34import Control.Apply (lift2)
35+import Control.Comonad.Cofree as Cofree
38import Control.Monad.Except (ExceptT(..), except, runExcept, throwError, withExcept)
36import Control.Monad.Except (ExceptT(..), except, runExcept, throwError, withExcept)
39import Data.Array as Array
37import Data.Array as Array
40import Data.Array.NonEmpty (NonEmptyArray, fromArray, toArray)
38import Data.Array.NonEmpty (NonEmptyArray, fromArray, toArray)
41import Data.Bifunctor (lmap)
39import Data.Bifunctor (lmap)
42import Data.DateTime (DateTime)
40import Data.DateTime (DateTime)
41+import Data.DateTime.Instant (Instant, instant, unInstant)
43import Data.Either (Either(..), hush, note)
42import Data.Either (Either(..), hush, note)
44import Data.Foldable (class Foldable, foldl)
43import Data.Foldable (class Foldable, foldl)
45import Data.FoldableWithIndex (foldrWithIndex)
44import Data.FoldableWithIndex (foldrWithIndex)
@@ -52,12 +51,13 @@ import Data.List.NonEmpty (NonEmptyList, singleton)
52import Data.Map (Map)
51import Data.Map (Map)
53import Data.Map as Map
52import Data.Map as Map
54import Data.Maybe (Maybe(..), fromMaybe, fromMaybe', maybe)
53import Data.Maybe (Maybe(..), fromMaybe, fromMaybe', maybe)
55−import Data.Newtype (class Newtype)
54+import Data.Newtype (class Newtype, unwrap, wrap)
56import Data.Nullable (Nullable, toMaybe, toNullable)
55import Data.Nullable (Nullable, toMaybe, toNullable)
57import Data.Number as Number
56import Data.Number as Number
58import Data.String.NonEmpty.Internal (NonEmptyString)
57import Data.String.NonEmpty.Internal (NonEmptyString)
59import Data.String.NonEmpty.Internal as NonEmptyString
58import Data.String.NonEmpty.Internal as NonEmptyString
60import Data.Symbol (class IsSymbol, reflectSymbol)
59import Data.Symbol (class IsSymbol, reflectSymbol)
60+import Data.Time.Duration (Milliseconds(..))
61import Data.Traversable (traverse)
61import Data.Traversable (traverse)
62import Data.Tuple (Tuple(..))
62import Data.Tuple (Tuple(..))
63import Data.Variant (Variant, inj, on)
63import Data.Variant (Variant, inj, on)
@@ -78,6 +78,8 @@ import Record.Builder (Builder)
78import Record.Builder as Builder
78import Record.Builder as Builder
79import Type.Prelude (Proxy(..))
79import Type.Prelude (Proxy(..))
80import Unsafe.Coerce (unsafeCoerce)
80import Unsafe.Coerce (unsafeCoerce)
81+import Yoga.Tree (Tree)
82+import Yoga.Tree as Tree
81
83
82-- | An alias for the Either result of decoding
84-- | An alias for the Either result of decoding
83type E a = Either MultipleErrors a
85type E a = Either MultipleErrors a
@@ -573,6 +575,22 @@ instance ReadForeign DateTime where
573 >>> note (pure $ ForeignError "Invalid date time")
575 >>> note (pure $ ForeignError "Invalid date time")
574 >>> except
576 >>> except
575
577
578+instance WriteForeign Instant where
579+ writeImpl = unInstant >>> writeImpl
580+
581+instance ReadForeign Instant where
582+ readImpl f = do
583+ millis ← readImpl f
584+ case instant millis of
585+ Nothing → except $ Left (pure $ ForeignError "Invalid instant")
586+ Just ins → pure ins
587+
588+instance WriteForeign Milliseconds where
589+ writeImpl = unwrap >>> writeImpl
590+
591+instance ReadForeign Milliseconds where
592+ readImpl = readImpl >>> map wrap
593+
576unsafeStringToInt ∷ String → Int
594unsafeStringToInt ∷ String → Int
577unsafeStringToInt = Int.fromString >>>
595unsafeStringToInt = Int.fromString >>>
578 (fromMaybe' \_ → unsafeCrashWith "impossible")
596 (fromMaybe' \_ → unsafeCrashWith "impossible")
@@ -593,3 +611,17 @@ sequenceCombining = foldl fn (Right mempty) >>> except
593 Left errs, Right _ → Left errs
611 Left errs, Right _ → Left errs
594 Right values, Right value → Right (values <> pure value)
612 Right values, Right value → Right (values <> pure value)
595 Right _, Left errs → Left errs
613 Right _, Left errs → Left errs
614+
615+instance WriteForeign a ⇒ WriteForeign (Tree a) where
616+ writeImpl ∷ Tree a → Foreign
617+ writeImpl t = write { value: Cofree.head t, children }
618+ where
619+ tail = Cofree.tail t
620+ children = if Array.null tail then Nothing else Just (writeImpl <$> tail)
621+
622+instance ReadForeign a ⇒ ReadForeign (Tree a) where
623+ readImpl f = do
624+ { value, children } ∷ { value ∷ a, children ∷ Maybe (Array Foreign) } ← readImpl f
625+ case children of
626+ Nothing → pure (Tree.leaf value)
627+ Just cs → traverse readImpl cs <#> Tree.mkTree value
test/BasicsSpec.purs+23−3
@@ -29,6 +29,7 @@ import Test.Util (roundtrips)
29import Type.Proxy (Proxy(..))
29import Type.Proxy (Proxy(..))
30import Yoga.JSON (class ReadForeign, class WriteForeign, readJSON, writeJSON)
30import Yoga.JSON (class ReadForeign, class WriteForeign, readJSON, writeJSON)
31import Yoga.JSON.Variant (TaggedVariant(..), UntaggedVariant(..))
31import Yoga.JSON.Variant (TaggedVariant(..), UntaggedVariant(..))
32+import Yoga.Tree (Tree, mkLeaf, mkTree, showTree)
32
33
33spec ∷ Spec Unit
34spec ∷ Spec Unit
34spec = describe "En- and decoding" $ do
35spec = describe "En- and decoding" $ do
@@ -144,11 +145,30 @@ spec = describe "En- and decoding" $ do
144
145
145 describe "works on non empty strings" do
146 describe "works on non empty strings" do
146 it "roundtrips NonEmptyString" do
147 it "roundtrips NonEmptyString" do
147− roundtrips (nes (Proxy :: Proxy "Non-Empty"))
148+ roundtrips (nes (Proxy ∷ Proxy "Non-Empty"))
148− it "fails to decode empty strings" do
149+ it "fails to decode empty strings" do
149− let (result :: (Either _ NonEmptyString)) = readJSON (show "")
150+ let (result ∷ (Either _ NonEmptyString)) = readJSON (show "")
150 result `shouldEqual` Left (pure $ ForeignError "String must not be empty")
151 result `shouldEqual` Left (pure $ ForeignError "String must not be empty")
151
152
153+ describe "works on trees" do
154+ it "roundtrips" do
155+ let t = mkTree "a" [ mkTree "b" [ mkLeaf "c", mkLeaf "d" ] ]
156+ roundtrips (ShowTree t)
157+
158+ it "encodes as expected" do
159+ let t = mkTree "a" [ mkTree "b" [ mkLeaf "c", mkLeaf "d" ] ]
160+ writeJSON (ShowTree t) `shouldEqual`
161+ """{"value":"a","children":[{"value":"b","children":[{"value":"c"},{"value":"d"}]}]}"""
162+
163+newtype ShowTree = ShowTree (Tree String)
164+
165+instance Show ShowTree where
166+ show (ShowTree t) = showTree t
167+
168+derive newtype instance WriteForeign ShowTree
169+derive newtype instance ReadForeign ShowTree
170+derive newtype instance Eq ShowTree
171+
152type ExampleVariant = ("erwin" ∷ String, "jackie" ∷ Int)
172type ExampleVariant = ("erwin" ∷ String, "jackie" ∷ Int)
153type ExampleTaggedVariant t v = TaggedVariant t v ExampleVariant
173type ExampleTaggedVariant t v = TaggedVariant t v ExampleVariant
154
174