packages feed

keiro-dsl-0.2.0.0: src/Keiro/Dsl/Manifest.hs

{- | The build-wiring __manifest__: a Cabal-pasteable summary of what a
@scaffold@ run produced. @scaffold@ writes @.hs@ files but the consumer still
has to wire them into a Cabal stanza by hand — the @other-modules@ list and the
@build-depends@ implied by the node kinds. This module renders both as plain
text a human pastes into a @.cabal@ file (see @keiro-dsl/keiro-dsl.cabal@'s
conformance stanzas for the hand-maintained version this replaces).

The dependency set is a pure function of which 'Node' constructors occur in the
spec. The mapping is grounded in the existing per-suite @build-depends@ in
@keiro-dsl/keiro-dsl.cabal@:

  * aggregate           => aeson, keiki, keiro, text     (keiro-dsl-conformance)
  * process             => aeson, keiki, keiro, shibuya-core, text, time, uuid
                                                         (…-process-runtime)
  * contract            => aeson, text                   (…-contract)
  * intake/emit/publisher (full integration path)
                        => effectful-core, hasql-transaction, keiro, kiroku-store
                                                         (…-intake-full)
  * workqueue           => aeson, keiro-pgmq, text       (…-queue, …-queue-runtime)
  * dispatch            => aeson, effectful-core, keiro-pgmq, text
                                                         (…-dispatch-full)
  * workflow/operation  => containers, effectful-core, keiro, text
                            (…-workflow-full; facts and runtime wiring only,
                             with the body hand-owned)

@base@ is always present.
-}
module Keiro.Dsl.Manifest (
    renderManifest,
    manifestDependencies,
    moduleNameOf,
) where

import Data.List (nub, sort)
import Data.Text (Text)
import Data.Text qualified as T
import Keiro.Dsl.Grammar
import Keiro.Dsl.Scaffold (ScaffoldModule (..))

{- | Render a Cabal-pasteable manifest from the modules a scaffold run produced
plus the node kinds present (which imply the dependency set). The first argument
names the source spec (for the header comment).
-}
renderManifest :: Text -> [ScaffoldModule] -> Spec -> Text
renderManifest specName mods spec =
    T.unlines $
        [ "-- keiro-dsl build manifest for " <> specName
        , "-- Paste the two blocks below into the consuming Cabal stanza."
        , "-- The generated layer is overwritten on every scaffold; hole modules are"
        , "-- create-if-absent (filled by hand)."
        , ""
        , "other-modules:"
        ]
            ++ map ("    " <>) (sort (map (moduleNameOf . modulePath) mods))
            ++ [ ""
               , "build-depends:"
               ]
            ++ map ("    , " <>) (manifestDependencies spec)

{- | The dotted module name recovered from a 'ScaffoldModule' path: drop the
trailing @.hs@ and replace @/@ with @.@.
-}
moduleNameOf :: FilePath -> Text
moduleNameOf p = T.replace "/" "." (T.dropEnd 3 (T.pack p))

{- | The sorted, deduplicated dependency set implied by the node kinds present
in the spec. @base@ is always included.
-}
manifestDependencies :: Spec -> [Text]
manifestDependencies spec =
    sort (nub ("base" : concatMap depsForNode (specNodes spec)))

-- | The dependencies a single node kind implies (see the module header table).
depsForNode :: Node -> [Text]
depsForNode n = case n of
    NAggregate{} -> ["aeson", "keiki", "keiro", "text"]
    NProcess{} -> ["aeson", "keiki", "keiro", "shibuya-core", "text", "time", "uuid"]
    NRouter{} -> ["effectful-core", "keiro", "shibuya-core", "text"]
    NContract{} -> ["aeson", "text"]
    NIntake{} -> integration
    NEmit{} -> integration
    NPublisher{} -> integration
    NWorkqueue{} -> ["aeson", "keiro-pgmq", "text"]
    NPgmqDispatch{} -> ["aeson", "effectful-core", "keiro-pgmq", "text"]
    NReadModel{} -> ["effectful-core", "hasql-transaction", "keiro", "kiroku-store", "text"]
    NWorkflow{} -> ["containers", "effectful-core", "keiro", "text"]
    NOperation{} -> ["effectful-core", "keiro", "text"]
  where
    integration = ["effectful-core", "hasql-transaction", "keiro", "kiroku-store"]