guardian-0.4.0.0: src/Development/Guardian/Types.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Development.Guardian.Types where
import Algebra.Graph (Graph)
import Algebra.Graph.AdjacencyMap.Algorithm (Cycle)
import qualified Algebra.Graph.Class as GC
import Algebra.Graph.Label
import qualified Algebra.Graph.Labelled as L
import Algebra.Graph.Relation.Preorder (PreorderRelation)
import Control.Applicative
import Control.Exception (Exception)
import Control.Monad.Trans.Reader (ReaderT (ReaderT, runReaderT))
import Data.Aeson (FromJSON (..), FromJSONKey, genericParseJSON, withObject, withText, (.:), (.:?))
import qualified Data.Aeson as J
import Data.Coerce (coerce)
import qualified Data.HashMap.Strict as HM
import Data.Hashable (Hashable)
import Data.Map (Map)
import qualified Data.Map.Strict as Map
import Data.Set (Set)
import Data.String (IsString)
import Data.Text (Text)
import qualified Data.Vector as V
import GHC.Generics (Generic)
type DomainGraph = PreorderRelation DomainName
type PackageGraph = Graph PackageName
type ActualGraph' e = L.Graph e DomainName
type ActualGraph = ActualGraph' (Path PackageName)
type PackageDic = Map PackageName (DomainName, V.Vector Dependency)
data DomainGraphError
= CyclicDomainDep (Cycle DomainName)
| OverlappingPackages (Map PackageName (Set DomainName))
deriving (Show, Eq, Ord, Generic)
deriving anyclass (Exception)
data CheckResult
= Ok
| OkWithDiagnostics Diagnostics
deriving (Show, Eq, Ord, Generic)
isEmptyDiagnostics :: Diagnostics -> Bool
isEmptyDiagnostics Diagnostics {..} =
Map.null redundantExtraDeps
-- && Set.null redundantDomainDeps
&& Map.null usedExceptionalRules
data Diagnostics = Diagnostics
{ redundantExtraDeps :: Map PackageName (Set Dependency)
, -- , redundantDomainDeps :: Set (DomainName, DomainName)
usedExceptionalRules :: Map PackageName (Set Dependency)
}
deriving (Show, Eq, Ord, Generic)
data DomainInfo = DomainInfo
{ domainConfig :: Domains
, domainGraph :: DomainGraph
, packageDic :: PackageDic
}
deriving (Show, Eq, Ord, Generic)
data PackageViolation
= DomainBoundaryViolation
{ fromDom :: DomainName
, toDom :: DomainName
, introducedBy :: [(PackageName, PackageName)]
}
| CyclicPackageDep (Cycle PackageName)
| UncoveredPackages [PackageName]
deriving (Show, Eq, Ord, Generic)
deriving anyclass (Exception)
newtype PackageName = PackageName {getPackageName :: Text}
deriving (Eq, Ord, Generic)
deriving newtype (Show, IsString, FromJSON, Hashable)
newtype DomainName = DomainName {getDomainName :: Text}
deriving (Eq, Ord, Generic)
deriving newtype (Show, IsString, FromJSON, FromJSONKey, Hashable)
data Domain = Domain
{ dependsOn :: Maybe (V.Vector DomainName)
, packages :: V.Vector PackageDef
}
deriving (Show, Eq, Ord, Generic)
instance FromJSON Domain where
parseJSON =
genericParseJSON
J.defaultOptions
{ J.omitNothingFields = True
, J.fieldLabelModifier = J.camelTo2 '_'
}
newtype Domains = Domains {domains :: HM.HashMap DomainName Domain}
deriving (Show, Eq, Ord, Generic)
deriving anyclass (FromJSON)
data PackageDef = PackageDef
{ packageName :: !PackageName
, extraDeps :: V.Vector Dependency
}
deriving (Show, Eq, Ord, Generic)
instance FromJSON PackageDef where
parseJSON =
runReaderT $
ReaderT (withText "package name" (pure . (`PackageDef` V.empty) . PackageName))
<|> ReaderT do
withObject "object" $ \dic -> do
packageName <- dic .: "package"
excepts <- dic .:? "exception"
extraDeps <- maybe (pure V.empty) (.: "depends_on") excepts
pure PackageDef {..}
data Dependency
= DomainDep !DomainName
| PackageDep !PackageName
deriving (Show, Eq, Ord, Generic)
instance FromJSON Dependency where
parseJSON =
runReaderT $
ReaderT (withText "domain name" (pure . DomainDep . DomainName))
<|> ReaderT
( withObject
"{package: ...} or {domain: ...}"
( \obj ->
DomainDep <$> obj .: "domain"
<|> PackageDep <$> obj .: "package"
)
)
newtype Overlayed gr = Overlayed {getOverlayed :: gr}
deriving (Show, Eq, Ord)
instance GC.Graph gr => Semigroup (Overlayed gr) where
(<>) = coerce $ GC.overlay @gr
instance GC.Graph gr => Monoid (Overlayed gr) where
mempty = Overlayed GC.empty