git.iko.soy/rowtype-yoga/purescript-yoga-json
on main help sign in
Instances for `Yoga.Tree` and `DateTime`

* Instances for `Yoga.Tree` probably not stack-safe * More datetime instances

commitf213e33fb42ed722f2b6a5f200486d7005e4d85a authorMark Eibes <mark.eibes@gmail.com> dateWed 23 Nov 2022, 16:57 (4 years ago) parentab7a5cb
4 files changed+63−9
packages.dhall+2−2 spago.dhall+2 src/Yoga/JSON.purs+36−4 test/BasicsSpec.purs+23−3
packages.dhall+2−2
@@ -1,5 +1,5 @@
11let upstream =
2− https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20221017/packages.dhall
3− sha256:88a9c22385f181da79caa7922502558b37a5fb759d45b45e904b105b8f88f6eb
2+ https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20221116/packages.dhall
3+ sha256:a0b88cbba414b046d9e7660fb77ed433d238d4c6ab20e75a479c9c6c823f2812
44
55in upstream
spago.dhall+2
@@ -12,6 +12,7 @@
1212 , "foldable-traversable"
1313 , "foreign"
1414 , "foreign-object"
15+ , "free"
1516 , "identity"
1617 , "integers"
1718 , "js-bigints"
@@ -31,6 +32,7 @@
3132 , "typelevel-prelude"
3233 , "unsafe-coerce"
3334 , "variant"
35+ , "yoga-tree"
3436 ]
3537, packages = ./packages.dhall
3638, sources = [ "src/**/*.purs" ]
src/Yoga/JSON.purs+36−4
@@ -11,7 +11,6 @@ module Yoga.JSON
1111 , parseJSON
1212 , undefined
1313 , unsafeStringify
14−
1514 , class ReadForeign
1615 , readImpl
1716 , class ReadForeignFields
@@ -21,25 +20,25 @@ module Yoga.JSON
2120 , class ReadTuple
2221 , readTupleImpl
2322 , tupleSize
24−
2523 , class WriteForeign
2624 , writeImpl
2725 , class WriteForeignFields
2826 , writeImplFields
2927 , class WriteForeignVariant
3028 , writeVariantImpl
31−
3229 ) where
3330
3431import Prelude
3532
3633import Control.Alt ((<|>))
3734import Control.Apply (lift2)
35+import Control.Comonad.Cofree as Cofree
3836import Control.Monad.Except (ExceptT(..), except, runExcept, throwError, withExcept)
3937import Data.Array as Array
4038import Data.Array.NonEmpty (NonEmptyArray, fromArray, toArray)
4139import Data.Bifunctor (lmap)
4240import Data.DateTime (DateTime)
41+import Data.DateTime.Instant (Instant, instant, unInstant)
4342import Data.Either (Either(..), hush, note)
4443import Data.Foldable (class Foldable, foldl)
4544import Data.FoldableWithIndex (foldrWithIndex)
@@ -52,12 +51,13 @@ import Data.List.NonEmpty (NonEmptyList, singleton)
5251import Data.Map (Map)
5352import Data.Map as Map
5453import Data.Maybe (Maybe(..), fromMaybe, fromMaybe', maybe)
55−import Data.Newtype (class Newtype)
54+import Data.Newtype (class Newtype, unwrap, wrap)
5655import Data.Nullable (Nullable, toMaybe, toNullable)
5756import Data.Number as Number
5857import Data.String.NonEmpty.Internal (NonEmptyString)
5958import Data.String.NonEmpty.Internal as NonEmptyString
6059import Data.Symbol (class IsSymbol, reflectSymbol)
60+import Data.Time.Duration (Milliseconds(..))
6161import Data.Traversable (traverse)
6262import Data.Tuple (Tuple(..))
6363import Data.Variant (Variant, inj, on)
@@ -78,6 +78,8 @@ import Record.Builder (Builder)
7878import Record.Builder as Builder
7979import Type.Prelude (Proxy(..))
8080import Unsafe.Coerce (unsafeCoerce)
81+import Yoga.Tree (Tree)
82+import Yoga.Tree as Tree
8183
8284-- | An alias for the Either result of decoding
8385type E a = Either MultipleErrors a
@@ -573,6 +575,22 @@ instance ReadForeign DateTime where
573575 >>> note (pure $ ForeignError "Invalid date time")
574576 >>> except
575577
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+
576594unsafeStringToInt ∷ String → Int
577595unsafeStringToInt = Int.fromString >>>
578596 (fromMaybe' \_ → unsafeCrashWith "impossible")
@@ -593,3 +611,17 @@ sequenceCombining = foldl fn (Right mempty) >>> except
593611 Left errs, Right _ → Left errs
594612 Right values, Right value → Right (values <> pure value)
595613 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)
2929import Type.Proxy (Proxy(..))
3030import Yoga.JSON (class ReadForeign, class WriteForeign, readJSON, writeJSON)
3131import Yoga.JSON.Variant (TaggedVariant(..), UntaggedVariant(..))
32+import Yoga.Tree (Tree, mkLeaf, mkTree, showTree)
3233
3334spec ∷ Spec Unit
3435spec = describe "En- and decoding" $ do
@@ -144,11 +145,30 @@ spec = describe "En- and decoding" $ do
144145
145146 describe "works on non empty strings" do
146147 it "roundtrips NonEmptyString" do
147− roundtrips (nes (Proxy :: Proxy "Non-Empty"))
148− it "fails to decode empty strings" do
149− let (result :: (Either _ NonEmptyString)) = readJSON (show "")
148+ roundtrips (nes (Proxy ∷ Proxy "Non-Empty"))
149+ it "fails to decode empty strings" do
150+ let (result ∷ (Either _ NonEmptyString)) = readJSON (show "")
150151 result `shouldEqual` Left (pure $ ForeignError "String must not be empty")
151152
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+
152172type ExampleVariant = ("erwin" ∷ String, "jackie" ∷ Int)
153173type ExampleTaggedVariant t v = TaggedVariant t v ExampleVariant
154174