hydra-0.15.0: src/main/haskell/Hydra/PackageRouting.hs
-- | Module-to-package routing for the Hydra packaging split.
--
-- A single source of truth for "which package owns which namespace" and
-- "which directory under dist/json/ does that package write to". Used by
-- both the JSON writers (update-json-*) and the JSON reader
-- (bootstrap-from-json), so the two sides can never disagree about where
-- a module's JSON file should live.
--
-- Today the mapping is a hardcoded prefix table. Eventually it should be
-- derived from each package's package.json (or Manifest.hs); see
-- feature_290_packaging-plan.md for the longer arc.
--
-- Routing contract:
--
-- namespaceToPackage :: Namespace -> String
--
-- Returns the package name (e.g. "hydra-kernel", "hydra-ext") that owns
-- a given Hydra namespace. Falls back to "hydra-kernel" for any
-- namespace that does not match an explicit prefix.
--
-- namespaceToPackageJsonDir :: FilePath -> Namespace -> FilePath
--
-- Given the dist-json root (e.g. "../../dist/json") and a namespace,
-- returns the absolute directory under which that module's JSON file
-- should be written: "<root>/<package>/src/main/json".
--
-- groupByPackage :: [Module] -> [(String, [Module])]
--
-- Partitions a list of modules by owning package, sorted by package
-- name for deterministic output ordering.
module Hydra.PackageRouting (
namespaceToPackage,
namespaceToPackageJsonDir,
namespaceToPackageTestJsonDir,
groupByPackage,
packagePrefixes,
defaultDistJsonRoot,
) where
import Hydra.Kernel
import Data.Function (on)
import Data.List (groupBy, isPrefixOf, sortOn)
import qualified System.FilePath as FP
-- | Default root under which per-package JSON trees live, relative to the
-- haskell head working directory (heads/haskell/). Callers can override
-- this when writing elsewhere (e.g. bootstrap-from-json's --ext-json-dir).
defaultDistJsonRoot :: FilePath
defaultDistJsonRoot = "../../dist/json"
-- | Map a module namespace to the package that owns it.
--
-- The ordering of 'packagePrefixes' matters: more specific prefixes must
-- come before less specific ones. The fallback "hydra-kernel" covers all
-- namespaces that don't match any explicit prefix.
namespaceToPackage :: Namespace -> String
namespaceToPackage (Namespace ns) = go packagePrefixes
where
go [] = "hydra-kernel"
go ((prefix, pkg) : rest)
| prefix `isPrefixOf` ns = pkg
| otherwise = go rest
-- | Given a dist-json root and a namespace, compute the directory under
-- which that module's JSON file should be written.
namespaceToPackageJsonDir :: FilePath -> Namespace -> FilePath
namespaceToPackageJsonDir root ns =
root FP.</> namespaceToPackage ns FP.</> "src" FP.</> "main" FP.</> "json"
-- | Like 'namespaceToPackageJsonDir' but for test JSON output.
namespaceToPackageTestJsonDir :: FilePath -> Namespace -> FilePath
namespaceToPackageTestJsonDir root ns =
root FP.</> namespaceToPackage ns FP.</> "src" FP.</> "test" FP.</> "json"
-- | Partition a list of modules by owning package, returning a list of
-- (packageName, modules) groups. The groups are sorted by package name
-- for deterministic output ordering.
groupByPackage :: [Module] -> [(String, [Module])]
groupByPackage mods =
fmap collapse
$ groupBy ((==) `on` fst)
$ sortOn fst
$ fmap (\m -> (namespaceToPackage (moduleNamespace m), m)) mods
where
collapse [] = ("", []) -- unreachable; groupBy never returns empty inner lists
collapse grp@((pkg, _) : _) = (pkg, fmap snd grp)
-- | Prefix-to-package table. Order matters: more-specific prefixes first.
-- Any namespace not matching any prefix falls through to "hydra-kernel".
packagePrefixes :: [(String, String)]
packagePrefixes =
[ -- Coder packages (main runtime modules)
("hydra.haskell.", "hydra-haskell")
, ("hydra.java.", "hydra-java")
, ("hydra.python.", "hydra-python")
, ("hydra.scala.", "hydra-scala")
, ("hydra.lisp.", "hydra-lisp")
, ("hydra.coq.", "hydra-coq")
, ("hydra.javaScript.", "hydra-javascript")
-- DSL wrapper modules for coder packages
, ("hydra.dsl.haskell.", "hydra-haskell")
, ("hydra.dsl.java.", "hydra-java")
, ("hydra.dsl.python.", "hydra-python")
, ("hydra.dsl.scala.", "hydra-scala")
, ("hydra.dsl.lisp.", "hydra-lisp")
, ("hydra.dsl.coq.", "hydra-coq")
, ("hydra.dsl.javaScript.", "hydra-javascript")
-- Synthesized decoder source modules for coder packages
, ("hydra.sources.decode.haskell.", "hydra-haskell")
, ("hydra.sources.decode.java.", "hydra-java")
, ("hydra.sources.decode.python.", "hydra-python")
, ("hydra.sources.decode.scala.", "hydra-scala")
, ("hydra.sources.decode.lisp.", "hydra-lisp")
, ("hydra.sources.decode.coq.", "hydra-coq")
, ("hydra.sources.decode.javaScript.", "hydra-javascript")
-- Synthesized encoder source modules for coder packages
, ("hydra.sources.encode.haskell.", "hydra-haskell")
, ("hydra.sources.encode.java.", "hydra-java")
, ("hydra.sources.encode.python.", "hydra-python")
, ("hydra.sources.encode.scala.", "hydra-scala")
, ("hydra.sources.encode.lisp.", "hydra-lisp")
, ("hydra.sources.encode.coq.", "hydra-coq")
, ("hydra.sources.encode.javaScript.", "hydra-javascript")
-- Property graph package
, ("hydra.pg.", "hydra-pg")
, ("hydra.cypher.", "hydra-pg")
, ("hydra.graphviz.", "hydra-pg")
, ("hydra.tinkerpop.", "hydra-pg")
, ("hydra.error.pg", "hydra-pg")
, ("hydra.validate.pg", "hydra-pg")
, ("hydra.decode.pg.", "hydra-pg")
, ("hydra.encode.pg.", "hydra-pg")
, ("hydra.sources.decode.pg.", "hydra-pg")
, ("hydra.sources.encode.pg.", "hydra-pg")
, ("hydra.demos.genpg.", "hydra-pg")
, ("openGql.grammar", "hydra-pg")
, ("com.gdblab.pathAlgebra.", "hydra-pg")
, ("hydra.dsl.pg.", "hydra-pg")
, ("hydra.dsl.cypher.", "hydra-pg")
, ("hydra.dsl.graphviz.", "hydra-pg")
, ("hydra.dsl.tinkerpop.", "hydra-pg")
, ("hydra.dsl.error.pg", "hydra-pg")
, ("hydra.dsl.openGql.", "hydra-pg")
, ("hydra.dsl.com.gdblab.pathAlgebra.", "hydra-pg")
-- RDF / OWL / SHACL / ShEx / XML schema package
, ("hydra.rdf.", "hydra-rdf")
, ("hydra.owl.", "hydra-rdf")
, ("hydra.shacl.", "hydra-rdf")
, ("hydra.shex.", "hydra-rdf")
, ("hydra.xml.schema", "hydra-rdf")
, ("hydra.dsl.rdf.", "hydra-rdf")
, ("hydra.dsl.owl.", "hydra-rdf")
, ("hydra.dsl.shacl.", "hydra-rdf")
, ("hydra.dsl.shex.", "hydra-rdf")
, ("hydra.dsl.xml.schema", "hydra-rdf")
-- WebAssembly package
, ("hydra.wasm.", "hydra-wasm")
, ("hydra.dsl.wasm.", "hydra-wasm")
-- Extension package (truly-ext coders: Avro, Protobuf, GraphQL, etc.)
, ("hydra.atlas", "hydra-ext")
, ("hydra.avro.", "hydra-ext")
, ("hydra.azure.", "hydra-ext")
, ("hydra.cpp.", "hydra-ext")
, ("hydra.csharp.", "hydra-ext")
, ("hydra.datalog.", "hydra-ext")
, ("hydra.delta.", "hydra-ext")
, ("hydra.geojson.", "hydra-ext")
, ("hydra.go.", "hydra-ext")
, ("hydra.graphql.", "hydra-ext")
, ("hydra.iana.", "hydra-ext")
, ("hydra.json.schema", "hydra-ext")
, ("hydra.kusto.", "hydra-ext")
, ("hydra.osv.", "hydra-ext")
, ("hydra.parquet.", "hydra-ext")
, ("hydra.pegasus.", "hydra-ext")
, ("hydra.protobuf.", "hydra-ext")
, ("hydra.rust.", "hydra-ext")
, ("hydra.sql.", "hydra-ext")
, ("hydra.stac.", "hydra-ext")
, ("hydra.typeScript.", "hydra-ext")
, ("hydra.workflow", "hydra-ext")
, ("hydra.dsl.atlas", "hydra-ext")
, ("hydra.dsl.avro.", "hydra-ext")
, ("hydra.dsl.azure.", "hydra-ext")
, ("hydra.dsl.cpp.", "hydra-ext")
, ("hydra.dsl.csharp.", "hydra-ext")
, ("hydra.dsl.datalog.", "hydra-ext")
, ("hydra.dsl.delta.", "hydra-ext")
, ("hydra.dsl.geojson.", "hydra-ext")
, ("hydra.dsl.go.", "hydra-ext")
, ("hydra.dsl.graphql.", "hydra-ext")
, ("hydra.dsl.iana.", "hydra-ext")
, ("hydra.dsl.json.schema", "hydra-ext")
, ("hydra.dsl.kusto.", "hydra-ext")
, ("hydra.dsl.osv.", "hydra-ext")
, ("hydra.dsl.parquet.", "hydra-ext")
, ("hydra.dsl.pegasus.", "hydra-ext")
, ("hydra.dsl.protobuf.", "hydra-ext")
, ("hydra.dsl.rust.", "hydra-ext")
, ("hydra.dsl.sql.", "hydra-ext")
, ("hydra.dsl.stac.", "hydra-ext")
, ("hydra.dsl.typeScript.", "hydra-ext")
, ("hydra.dsl.workflow", "hydra-ext")
-- hydra.yaml.model lives in hydra-kernel, so we route the hydra-ext yaml
-- modules (coder, language, serde) explicitly rather than with a blanket
-- hydra.yaml. prefix.
, ("hydra.yaml.coder", "hydra-ext")
, ("hydra.yaml.language", "hydra-ext")
, ("hydra.yaml.serde", "hydra-ext")
]