packages feed

graphwiz-3.0.0: src/Text/Dot/Types.hs

{-# LANGUAGE TemplateHaskell #-}

module Text.Dot.Types where

import "this" Prelude

import Control.Lens
import Data.Hashable
import TextBuilder    (TextBuilder)


--------------------------------------------------------------------------------
-- Entities

-- | Represents the type of a graph entity.
--
-- DOT distinguishes between graphs, nodes, edges, and subgraphs /
-- clusters. This type differs slightly: it does not have a value for graphs, as it
-- is never required, and we differentiate subgraphs and clusters.
--
-- This type is used internally to distinguish entities, mostly for the purpose
-- of default attributes (see 'Text.Dot.defaults').
data EntityType = Node | Edge | Subgraph | Cluster
  deriving (Show, Eq, Ord, Enum, Bounded)

instance Hashable EntityType where
  hashWithSalt s e = hashWithSalt s (fromEnum e)

-- | Opaque identifier for graph entities.
--
-- This type uniquely identifies an entity within the graph. To create one, see
-- 'Text.Dot.node', 'Text.Dot.edge', 'Text.Dot.subgraph', or 'Text.Dot.cluster'.
data Entity = Entity EntityType Int
  deriving (Eq, Ord)

instance Hashable Entity where
  hashWithSalt s (Entity t i) = s `hashWithSalt` t `hashWithSalt` i

-- | Retrieves the type of a given t'Entity'.
getType :: Entity -> EntityType
getType (Entity t _) = t

-- | Unique entity of the top-level graph.
rootGraph :: Entity
rootGraph = Entity Subgraph (-1)

-- | Describes types that can be used as the end of an edge.
--
-- When declaring an edge, each node can be described in two different ways:
-- either via its 'Entity', or by using a 'Text' name.
--
-- For more information, see 'Text.Dot.edge', 'Text.Dot.register', and
-- 'Text.Dot.retrieve'.
class ToEdgeNode a where
  toEdgeNode :: a -> EdgeNode

instance ToEdgeNode Entity where
  toEdgeNode = KnownNode

instance ToEdgeNode EdgeNode where
  toEdgeNode = id

-- | Constructs an edge node from the given text name.
--
-- This allows for an edge to be declared using the name given to this
-- function. The name won't be resolved immediately, but must be registered at
-- some point within the graph.
--
-- > digraph do
-- >   node "A"
-- >   registerItAs "a"
-- >
-- >   edge (retrieve "a") (retrieve "b")
-- >
-- >   node "B"
-- >   registerItAs "b"
--
-- See also 'Text.Dot.edge'.
retrieve :: Text -> EdgeNode
retrieve = UnknownNode


--------------------------------------------------------------------------------
-- Internal state

-- | Opaque internal type.
--
-- Represent one end of an edge: either an 'Entity', or an arbitrary 'Text'
-- name.
--
-- See also 'Text.Dot.edge' and 'retrieve'.
data EdgeNode
  = KnownNode Entity
  | UnknownNode Text

-- | An entity's attributes.
--
-- Attributes are untyped, and are a simple mapping from 'Text' to 'Text', for
-- flexibility.
type Attributes = HashMap Text Text

-- | A path through the graph.
--
-- This opaque type represents the path from the root to the current scope. The
-- current path can be obtained via 'Text.Dot.currentPath'.
newtype Path = Path { unwrapPath :: NonEmpty Entity }

makePrisms ''Path

type DotContext = [Entity]

data EdgeInfo = EdgeInfo EdgeNode EdgeNode (Maybe Entity) (Maybe Entity)

-- | Internal opaque graph state.
data DotGraph = DotGraph
  { _defaultAttributes :: HashMap EntityType Attributes
  , _entityAttributes  :: HashMap Entity Attributes
  , _edgeInfo          :: HashMap Entity EdgeInfo
  , _subgraphInfo      :: HashMap Entity DotContext
  , _entityRegister    :: HashMap Text Entity
  , _contextStack      :: NonEmpty DotContext
  , _entityIndex       :: Int
  , _latest            :: Entity
  }

makeLenses ''DotGraph

initialGraph :: DotGraph
initialGraph = DotGraph mempty mempty mempty mempty mempty (pure mempty) 0 rootGraph

data RenderState = RenderState
  { _knownNodes   :: HashSet Entity
  , _delayedEdges :: [TextBuilder]
  }

makeLenses ''RenderState

initialRenderState :: RenderState
initialRenderState = RenderState mempty mempty