{-# LANGUAGE BangPatterns #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE DeriveGeneric #-} {-# language OverloadedStrings #-} {-# options_ghc -Wno-unused-imports #-} {-| Module : Algebra.Graph.IO.JSON Description : 'aeson' instances for algebraic-graphs types Copyright : (c) Marco Zocca, 2022 Maintainer : ocramz Stability : experimental Portability : POSIX Orphan instances for compatibility between 'algebraic-graphs' and 'aeson'. Import only if you know what you're doing. -} module Algebra.Graph.IO.JSON () where import Control.Applicative (Alternative(..)) import GHC.Generics (Generic) -- aeson import qualified Data.Aeson as A (FromJSON(..), ToJSON(..), encode, eitherDecode, Value, withArray, withText, withObject, (.:), Object) import qualified Data.Aeson.Types as A (Parser) import qualified Data.Aeson.Encoding as A (value, fromEncoding) -- alga import qualified Algebra.Graph as G (Graph(..), edges, foldg) import qualified Algebra.Graph.Labelled as GL (Graph(..), edges, foldg) -- unlabeled edges instance A.ToJSON a => A.ToJSON (G.Graph a) where toJSON = graphToValue graphToValue :: (A.ToJSON t) => G.Graph t -> A.Value graphToValue = go where go G.Empty = A.toJSON Empty go (G.Vertex x) = A.toJSON (Vertex x) go (G.Overlay x y) = A.toJSON (Overlay (go x) (go y)) go (G.Connect x y) = A.toJSON (Connect (go x) (go y)) instance A.FromJSON a => A.FromJSON (G.Graph a) where parseJSON x = A.withObject "Graph" gObj x <|> parseE x gObj :: A.FromJSON a => A.Object -> A.Parser (G.Graph a) gObj o = parseC o <|> parseO o <|> parseV o parseE :: A.Value -> A.Parser (G.Graph a) parseE = A.withArray "empty" $ \t -> if null t then pure G.Empty else fail "cannot parse Empty" parseV :: A.FromJSON a => A.Object -> A.Parser (G.Graph a) parseV o = G.Vertex <$> o A..: "v" parseO :: A.FromJSON a => A.Object -> A.Parser (G.Graph a) parseO o = do a <- o A..: "o1" b <- o A..: "o2" pure $ G.Overlay a b parseC :: A.FromJSON a => A.Object -> A.Parser (G.Graph a) parseC o = do a <- o A..: "c1" b <- o A..: "c2" pure $ G.Connect a b instance (A.ToJSON a, A.ToJSON e) => A.ToJSON (GL.Graph e a) where toJSON = graphLToValue graphLToValue :: (A.ToJSON a, A.ToJSON e) => GL.Graph e a -> A.Value graphLToValue = go where go GL.Empty = A.toJSON Empty go (GL.Vertex x) = A.toJSON (Vertex x) go (GL.Connect e x y) = A.toJSON (LEdge e (go x) (go y)) instance (A.FromJSON e, A.FromJSON a) => A.FromJSON (GL.Graph e a) where parseJSON x = A.withObject "Graph (labeled)" gLObj x <|> parseEL x gLObj :: (A.FromJSON e, A.FromJSON a) => A.Object -> A.Parser (GL.Graph e a) gLObj o = parseCL o <|> parseVL o parseEL :: A.Value -> A.Parser (GL.Graph e a) parseEL = A.withArray "empty" $ \t -> if null t then pure GL.Empty else fail "cannot parse Empty" parseVL :: A.FromJSON a => A.Object -> A.Parser (GL.Graph e a) parseVL o = GL.Vertex <$> o A..: "v" parseCL :: (A.FromJSON e, A.FromJSON a) => A.Object -> A.Parser (GL.Graph e a) parseCL o = do e <- o A..: "l" a <- o A..: "e1" b <- o A..: "e2" pure $ GL.Connect e a b -- | Helper types -- empty data Empty = Empty deriving (Eq, Show, Generic) instance A.ToJSON Empty -- vertex newtype Vertex a = Vertex { v :: a } deriving (Eq, Show, Generic) instance A.ToJSON a => A.ToJSON (Vertex a) -- overlay data Overlay a = Overlay { o1 :: a, o2 :: a} deriving (Eq, Show, Generic) instance A.ToJSON a => A.ToJSON (Overlay a) -- connect data Connect a = Connect { c1 :: a, c2 :: a} deriving (Eq, Show, Generic) instance A.ToJSON a => A.ToJSON (Connect a) data LEdge e a = LEdge { l :: e, e1 :: a, e2 :: a } deriving (Eq, Show, Generic) instance (A.ToJSON e, A.ToJSON a) => A.ToJSON (LEdge e a)