packages feed

packstream-0.1.0.1: src/Data/PackStream/Structure.hs

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
module Data.PackStream.Structure
( ToStructure (..), FromStructure (..)
, Node (..), Relationship (..), UnboundRelationship (..), Path (..)
-- , Date (..), Time (..), LocalTime (..), DateTime (..), DateTimeZoneId (..), LocalDateTime (..)
-- , Duration (..)
-- , Point2D (..), Point3D (..)
) where

import Data.Text (Text)
import Data.Map.Strict (Map)
import Control.Monad.Except (MonadError(..))
import Control.Monad ((>=>))

import Data.PackStream.Internal.Type

-- * Structure coverters
-- 
-- 'PackStream' protocol provides several built-in 'Structure' types. They
-- are the objects with specific fields and one-byte structure signature.
-- The 'ToStructure' and 'FromStructure' typeclasses help to convert
-- Haskell representation of these objects into and from generic 'Structure'
-- type.

-- |The set of types, that can be presented as 'PackStream' 'Structure's
class ToStructure a where
    -- |Convert object to 'Structure'
    toStructure :: a -> Structure

-- |The set of types, that can be parsed from 'PackStream' 'Structure's
class FromStructure a where
    -- |Convert 'Structure' to the object of selected type
    fromStructure :: Structure -> Either PackStreamError a

-- * Built-in structure types

-- |Snapshot of a node within a graph database
data Node = Node { nodeId    :: Int            -- ^Node identifier
                 , labels    :: [Text]         -- ^List of node labels
                 , nodeProps :: Map Text Value -- ^Dict of node properties
                 }
  deriving (Show, Eq)

instance ToStructure Node where
    toStructure Node{..} = Structure 0x4E [ toValue nodeId
                                          , toValue labels
                                          , toValue nodeProps
                                          ]

instance FromStructure Node where
    fromStructure (Structure 0x4E [I nid, L lbls, D nps]) = Node nid <$> traverse fromValue lbls <*> pure nps
    fromStructure _                                       = throwError $ WrongStructure "Node"

-- |Snapshot of a relationship within a graph database
data Relationship = Relationship { relId       :: Int            -- ^Relationship identifier
                                 , startNodeId :: Int            -- ^Identifier of the start node
                                 , endNodeId   :: Int            -- ^Identifier of the end node
                                 , relType     :: Text           -- ^Relationship type
                                 , relProps    :: Map Text Value -- ^Dict of relationship properties
                                 }
  deriving (Show, Eq)

instance ToStructure Relationship where
    toStructure Relationship{..} = Structure 0x52 [ toValue relId
                                                  , toValue startNodeId
                                                  , toValue endNodeId
                                                  , toValue relType
                                                  , toValue relProps
                                                  ]

instance FromStructure Relationship where
    fromStructure (Structure 0x52 [I rid, I snid, I enid, T rt, D rps]) = pure $ Relationship rid snid enid rt rps
    fromStructure _                                                     = throwError $ WrongStructure "Relationship"

-- |Relationship detail without start or end node information
data UnboundRelationship = UnboundRelationship { urelId    :: Int            -- ^Relationship identifier
                                               , urelType  :: Text           -- ^Relationship type
                                               , urelProps :: Map Text Value -- ^Dict of relationship properties
                                               }
  deriving (Show, Eq)

instance ToStructure UnboundRelationship where
    toStructure UnboundRelationship{..} = Structure 0x72 [ toValue urelId
                                                         , toValue urelType
                                                         , toValue urelProps
                                                         ]

instance FromStructure UnboundRelationship where
    fromStructure (Structure 0x72 [I rid, T rt, D rps]) = pure $ UnboundRelationship rid rt rps
    fromStructure _                                     = throwError $ WrongStructure "UnboundRelationship"

-- |Alternating sequence of nodes and relationships
data Path = Path { nodes :: [Node]                -- ^Chain of 'Node's in path
                 , rels  :: [UnboundRelationship] -- ^Chain of 'UnboundRelationship's in path
                 , ids   :: [Int]                 -- ^The ids is a list of relationship id and node id to represent the path
                 }
  deriving (Show, Eq)

instance ToStructure Path where
    toStructure Path{..} = Structure 0x50 [ toValue $ fmap toStructure nodes
                                          , toValue $ fmap toStructure rels
                                          , toValue ids
                                          ]

instance FromStructure Path where
    fromStructure (Structure 0x50 [L nds, L rls, L is]) = Path <$> traverse (fromValue >=> fromStructure) nds 
                                                               <*> traverse (fromValue >=> fromStructure) rls 
                                                               <*> traverse fromValue is
    fromStructure _                                     = throwError $ WrongStructure "Path"

-- |The days are days since the Unix epoch
newtype Date = Date { days :: Int -- ^The days are days since the Unix epoch
                    }
  deriving (Show, Eq)

instance ToStructure Date where
  toStructure Date{..} = Structure 0x44 [toValue days]

instance FromStructure Date where
  fromStructure (Structure 0x44 [I ds]) = pure $ Date ds
  fromStructure _                       = throwError $ WrongStructure "Date"

-- data Time
-- data LocalTime
-- data DateTime
-- data DateTimeZoneId
-- data LocalDateTime
-- data Duration
-- data Point2D
-- data Point3D