packages feed

hydra-kernel-0.16.0: src/main/haskell/Hydra/Validate/Packaging.hs

-- Note: this is an automatically generated file. Do not edit.
-- | Validation functions for modules and packages

module Hydra.Validate.Packaging where
import qualified Hydra.Annotations as Annotations
import qualified Hydra.Ast as Ast
import qualified Hydra.Coders as Coders
import qualified Hydra.Constants as Constants
import qualified Hydra.Core as Core
import qualified Hydra.Error.Checking as Checking
import qualified Hydra.Error.Core as ErrorCore
import qualified Hydra.Error.Packaging as ErrorPackaging
import qualified Hydra.Errors as Errors
import qualified Hydra.Formatting as Formatting
import qualified Hydra.Graph as Graph
import qualified Hydra.Json.Model as Model
import qualified Hydra.Haskell.Lib.Equality as Equality
import qualified Hydra.Haskell.Lib.Lists as Lists
import qualified Hydra.Haskell.Lib.Logic as Logic
import qualified Hydra.Haskell.Lib.Maps as Maps
import qualified Hydra.Haskell.Lib.Optionals as Optionals
import qualified Hydra.Haskell.Lib.Pairs as Pairs
import qualified Hydra.Haskell.Lib.Regex as Regex
import qualified Hydra.Haskell.Lib.Sets as Sets
import qualified Hydra.Haskell.Lib.Strings as Strings
import qualified Hydra.Names as Names
import qualified Hydra.Packaging as Packaging
import qualified Hydra.Parsing as Parsing
import qualified Hydra.Paths as Paths
import qualified Hydra.Query as Query
import qualified Hydra.Relational as Relational
import qualified Hydra.Tabular as Tabular
import qualified Hydra.Testing as Testing
import qualified Hydra.Topology as Topology
import qualified Hydra.Typed as Typed
import qualified Hydra.Typing as Typing
import qualified Hydra.Util as Util
import qualified Hydra.Validation as Validation
import qualified Hydra.Variants as Variants
import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
import qualified Data.Scientific as Sci
-- | Append a rule-tagged InvalidModuleError finding to a ValidationResult, classifying as error or warning per the profile and respecting maxErrors/maxWarnings bounds.
appendFindingModule :: Validation.ValidationProfile -> Validation.ValidationResult t0 -> Maybe (Core.Name, t0) -> Validation.ValidationResult t0
appendFindingModule p acc finding =
    Optionals.cases finding acc (\rp ->
      let ruleName = Pairs.first rp
          payload = Pairs.second rp
          errs = Validation.validationResultErrors acc
          wrns = Validation.validationResultWarnings acc
      in (Logic.ifElse (Sets.member ruleName (Validation.validationProfileErrorRules p)) (Logic.ifElse (Equality.lt (Lists.length errs) (Validation.validationProfileMaxErrors p)) (Validation.ValidationResult {
        Validation.validationResultErrors = (Lists.concat2 errs (Lists.singleton payload)),
        Validation.validationResultWarnings = wrns}) acc) (Logic.ifElse (Sets.member ruleName (Validation.validationProfileWarningRules p)) (Logic.ifElse (Equality.lt (Lists.length wrns) (Validation.validationProfileMaxWarnings p)) (Validation.ValidationResult {
        Validation.validationResultErrors = errs,
        Validation.validationResultWarnings = (Lists.concat2 wrns (Lists.singleton payload))}) acc) acc)))
-- | Append a rule-tagged InvalidPackageError finding to a ValidationResult, classifying as error or warning per the profile and respecting maxErrors/maxWarnings bounds.
appendFindingPackage :: Validation.ValidationProfile -> Validation.ValidationResult t0 -> Maybe (Core.Name, t0) -> Validation.ValidationResult t0
appendFindingPackage p acc finding =
    Optionals.cases finding acc (\rp ->
      let ruleName = Pairs.first rp
          payload = Pairs.second rp
          errs = Validation.validationResultErrors acc
          wrns = Validation.validationResultWarnings acc
      in (Logic.ifElse (Sets.member ruleName (Validation.validationProfileErrorRules p)) (Logic.ifElse (Equality.lt (Lists.length errs) (Validation.validationProfileMaxErrors p)) (Validation.ValidationResult {
        Validation.validationResultErrors = (Lists.concat2 errs (Lists.singleton payload)),
        Validation.validationResultWarnings = wrns}) acc) (Logic.ifElse (Sets.member ruleName (Validation.validationProfileWarningRules p)) (Logic.ifElse (Equality.lt (Lists.length wrns) (Validation.validationProfileMaxWarnings p)) (Validation.ValidationResult {
        Validation.validationResultErrors = errs,
        Validation.validationResultWarnings = (Lists.concat2 wrns (Lists.singleton payload))}) acc) acc)))
-- | Check for module namespaces that conflict when mapped to target language paths
checkConflictingModuleNames :: Packaging.Package -> Maybe ErrorPackaging.InvalidPackageError
checkConflictingModuleNames pkg =

      let result =
              Lists.foldl (\acc -> \mod ->
                let seen = Pairs.first acc
                    err = Pairs.second acc
                in (Optionals.cases err (
                  let ns = Packaging.moduleName mod
                      key = Strings.toLower (Packaging.unModuleName ns)
                      existing = Maps.lookup key seen
                  in (Optionals.cases existing (Maps.insert key ns seen, Nothing) (\first -> (
                    seen,
                    (Just (ErrorPackaging.InvalidPackageErrorConflictingModuleName (ErrorPackaging.ConflictingModuleNameError {
                      ErrorPackaging.conflictingModuleNameErrorFirst = first,
                      ErrorPackaging.conflictingModuleNameErrorSecond = ns}))))))) (\_ -> acc))) (Maps.empty, Nothing) (Packaging.packageModules pkg)
      in (Pairs.second result)
-- | Check for union variant names that, when mapped to constructor names, conflict with other type definitions
checkConflictingVariantNames :: Packaging.Module -> Maybe ErrorPackaging.InvalidModuleError
checkConflictingVariantNames mod =

      let ns = Packaging.moduleName mod
          defs = Packaging.moduleDefinitions mod
          defNames = Lists.foldl (\acc -> \def -> Sets.insert (Names.localNameOf (definitionName def)) acc) Sets.empty defs
      in (Lists.foldl (\acc -> \def -> Optionals.cases acc (case def of
        Packaging.DefinitionType v0 ->
          let typeName = Packaging.typeDefinitionName v0
              localTypeName = Names.localNameOf typeName
              typ = Core.typeSchemeBody (Packaging.typeDefinitionBody v0)
          in case typ of
            Core.TypeUnion v1 -> Lists.foldl (\innerAcc -> \field -> Optionals.cases innerAcc (
              let fieldName = Core.fieldTypeName field
                  localFieldName = Names.localNameOf fieldName
                  constructorName = Strings.cat2 (Formatting.capitalize localTypeName) (Formatting.capitalize localFieldName)
              in (Logic.ifElse (Sets.member constructorName defNames) (Just (ErrorPackaging.InvalidModuleErrorConflictingVariantName (ErrorPackaging.ConflictingVariantNameError {
                ErrorPackaging.conflictingVariantNameErrorModuleName = ns,
                ErrorPackaging.conflictingVariantNameErrorTypeName = typeName,
                ErrorPackaging.conflictingVariantNameErrorVariantName = fieldName,
                ErrorPackaging.conflictingVariantNameErrorConflictingName = (Core.Name constructorName)}))) Nothing)) (\_ -> innerAcc)) Nothing v1
            _ -> Nothing
        _ -> Nothing) (\_ -> acc)) Nothing defs)
-- | Check that every top-level definition is wrapped in a description annotation
checkDefinitionDocumentation :: Packaging.Module -> Maybe ErrorPackaging.InvalidModuleError
checkDefinitionDocumentation mod =

      let ns = Packaging.moduleName mod
      in (Lists.foldl (\acc -> \def -> Optionals.cases acc (
        let name = definitionName def
            documented =
                    case def of
                      Packaging.DefinitionTerm v0 ->
                        let term = Packaging.termDefinitionBody v0
                        in case term of
                          Core.TermAnnotated v1 -> Annotations.hasDescription (Annotations.getAnnotationMap (Core.annotatedTermAnnotation v1))
                          _ -> False
                      Packaging.DefinitionType v0 ->
                        let typ = Core.typeSchemeBody (Packaging.typeDefinitionBody v0)
                        in case typ of
                          Core.TypeAnnotated v1 -> Annotations.hasDescription (Annotations.getAnnotationMap (Core.annotatedTypeAnnotation v1))
                          _ -> False
                      Packaging.DefinitionPrimitive v0 -> Optionals.cases (Packaging.primitiveDefinitionMetadata v0) False (\em -> Logic.not (Equality.equal (Optionals.fromOptional "" (Packaging.entityMetadataDescription em)) ""))
                      _ -> False
        in (Logic.ifElse documented Nothing (Just (ErrorPackaging.InvalidModuleErrorMissingDocumentation (ErrorPackaging.MissingDocumentationError {
          ErrorPackaging.missingDocumentationErrorModuleName = ns,
          ErrorPackaging.missingDocumentationErrorName = name}))))) (\_ -> acc)) Nothing (Packaging.moduleDefinitions mod))
-- | Check that all definition names in a module have the module's name as a prefix
checkDefinitionModuleNames :: Packaging.Module -> Maybe ErrorPackaging.InvalidModuleError
checkDefinitionModuleNames mod =

      let ns = Packaging.moduleName mod
          prefix = Strings.cat2 (Packaging.unModuleName ns) "."
          prefixLen = Strings.length prefix
      in (Lists.foldl (\acc -> \def -> Optionals.cases acc (
        let name = definitionName def
            nameStr = Core.unName name
            namePrefix = Lists.take prefixLen (Strings.toList nameStr)
        in (Logic.ifElse (Equality.equal (Strings.fromList namePrefix) prefix) Nothing (Just (ErrorPackaging.InvalidModuleErrorDefinitionNotInModuleName (ErrorPackaging.DefinitionNotInModuleNameError {
          ErrorPackaging.definitionNotInModuleNameErrorModuleName = ns,
          ErrorPackaging.definitionNotInModuleNameErrorName = name}))))) (\_ -> acc)) Nothing (Packaging.moduleDefinitions mod))
-- | Check that term definitions have camelCase local names and type definitions have PascalCase local names
checkDefinitionNameConvention :: Packaging.Module -> Maybe ErrorPackaging.InvalidModuleError
checkDefinitionNameConvention mod =

      let ns = Packaging.moduleName mod
      in (Lists.foldl (\acc -> \def -> Optionals.cases acc (
        let name = definitionName def
            local = Names.localNameOf name
            expected =
                    case def of
                      Packaging.DefinitionTerm _ -> Util.CaseConventionCamel
                      Packaging.DefinitionType _ -> Util.CaseConventionPascal
                      Packaging.DefinitionPrimitive _ -> Util.CaseConventionCamel
                      _ -> Util.CaseConventionCamel
            pattern =
                    case def of
                      Packaging.DefinitionTerm _ -> Constants.regexCamelCase
                      Packaging.DefinitionType _ -> Constants.regexPascalCase
                      Packaging.DefinitionPrimitive _ -> Constants.regexCamelCase
                      _ -> Constants.regexCamelCase
        in (Logic.ifElse (Regex.matches pattern local) Nothing (Just (ErrorPackaging.InvalidModuleErrorInvalidDefinitionName (ErrorPackaging.InvalidDefinitionNameError {
          ErrorPackaging.invalidDefinitionNameErrorModuleName = ns,
          ErrorPackaging.invalidDefinitionNameErrorName = name,
          ErrorPackaging.invalidDefinitionNameErrorExpectedConvention = expected}))))) (\_ -> acc)) Nothing (Packaging.moduleDefinitions mod))
-- | Check that a module's definitions list is sorted in ascending lexicographic order by local name
checkDefinitionOrdering :: Packaging.Module -> Maybe ErrorPackaging.InvalidModuleError
checkDefinitionOrdering mod =

      let ns = Packaging.moduleName mod
          result =
                  Lists.foldl (\acc -> \def ->
                    let prev = Pairs.first acc
                        err = Pairs.second acc
                    in (Optionals.cases err (
                      let currName = definitionName def
                          currLocal = Names.localNameOf currName
                      in (Optionals.cases prev (Just currName, Nothing) (\prevName ->
                        let prevLocal = Names.localNameOf prevName
                        in (Logic.ifElse (Equality.lt prevLocal currLocal) (Just currName, Nothing) (
                          Just currName,
                          (Just (ErrorPackaging.InvalidModuleErrorDefinitionsOutOfOrder (ErrorPackaging.DefinitionsOutOfOrderError {
                            ErrorPackaging.definitionsOutOfOrderErrorModuleName = ns,
                            ErrorPackaging.definitionsOutOfOrderErrorPrecedingName = prevName,
                            ErrorPackaging.definitionsOutOfOrderErrorFollowingName = currName})))))))) (\_ -> acc))) (Nothing, Nothing) (Packaging.moduleDefinitions mod)
      in (Pairs.second result)
-- | Check for duplicate definition names in a module
checkDuplicateDefinitionNames :: Packaging.Module -> Maybe ErrorPackaging.InvalidModuleError
checkDuplicateDefinitionNames mod =

      let ns = Packaging.moduleName mod
          result =
                  Lists.foldl (\acc -> \def ->
                    let seen = Pairs.first acc
                        err = Pairs.second acc
                    in (Optionals.cases err (
                      let name = definitionName def
                      in (Logic.ifElse (Sets.member name seen) (
                        seen,
                        (Just (ErrorPackaging.InvalidModuleErrorDuplicateDefinitionName (ErrorPackaging.DuplicateDefinitionNameError {
                          ErrorPackaging.duplicateDefinitionNameErrorModuleName = ns,
                          ErrorPackaging.duplicateDefinitionNameErrorName = name})))) (Sets.insert name seen, Nothing))) (\_ -> acc))) (Sets.empty, Nothing) (Packaging.moduleDefinitions mod)
      in (Pairs.second result)
-- | Check for duplicate module namespaces in a package
checkDuplicateModuleNames :: Packaging.Package -> Maybe ErrorPackaging.InvalidPackageError
checkDuplicateModuleNames pkg =

      let result =
              Lists.foldl (\acc -> \mod ->
                let seen = Pairs.first acc
                    err = Pairs.second acc
                in (Optionals.cases err (
                  let ns = Packaging.moduleName mod
                  in (Logic.ifElse (Sets.member ns seen) (
                    seen,
                    (Just (ErrorPackaging.InvalidPackageErrorDuplicateModuleName (ErrorPackaging.DuplicateModuleNameError {
                      ErrorPackaging.duplicateModuleNameErrorModuleName = ns})))) (Sets.insert ns seen, Nothing))) (\_ -> acc))) (Sets.empty, Nothing) (Packaging.packageModules pkg)
      in (Pairs.second result)
-- | Check that the module's namespace matches the dotted-lowercase naming convention
checkModuleNameConvention :: Packaging.Module -> Maybe ErrorPackaging.InvalidModuleError
checkModuleNameConvention mod =

      let ns = Packaging.moduleName mod
      in (Logic.ifElse (Regex.matches Constants.regexNamespace (Packaging.unModuleName ns)) Nothing (Just (ErrorPackaging.InvalidModuleErrorInvalidModuleNameConvention (ErrorPackaging.InvalidModuleNameConventionError {
        ErrorPackaging.invalidModuleNameConventionErrorModuleName = ns}))))
-- | Check that the package's name matches the hyphen-separated lowercase naming convention
checkPackageNameConvention :: Packaging.Package -> Maybe ErrorPackaging.InvalidPackageError
checkPackageNameConvention pkg =

      let pname = Packaging.packageName pkg
      in (Logic.ifElse (Regex.matches Constants.regexPackageName (Packaging.unPackageName pname)) Nothing (Just (ErrorPackaging.InvalidPackageErrorInvalidPackageName (ErrorPackaging.InvalidPackageNameError {
        ErrorPackaging.invalidPackageNameErrorPackageName = pname}))))
-- | Extract the name from a definition
definitionName :: Packaging.Definition -> Core.Name
definitionName def =
    case def of
      Packaging.DefinitionTerm v0 -> Packaging.termDefinitionName v0
      Packaging.DefinitionType v0 -> Packaging.typeDefinitionName v0
      Packaging.DefinitionPrimitive v0 -> Packaging.primitiveDefinitionName v0
-- | True iff the given rule name appears in the profile's errorRules or warningRules.
enabledPackaging :: Validation.ValidationProfile -> Core.Name -> Bool
enabledPackaging p ruleName =
    Logic.or (Sets.member ruleName (Validation.validationProfileErrorRules p)) (Sets.member ruleName (Validation.validationProfileWarningRules p))
-- | The default validation profile for module/package validation. Every kernel-shipped check classified as an error; no warnings; maxErrors=1, maxWarnings=20.
kernelDefaultPackagingProfile :: Validation.ValidationProfile
kernelDefaultPackagingProfile =
    Validation.ValidationProfile {
      Validation.validationProfileErrorRules = (Sets.fromList [
        Core.Name "hydra.error.packaging.InvalidModuleError.conflictingVariantName",
        (Core.Name "hydra.error.packaging.InvalidModuleError.definitionNotInModuleName"),
        (Core.Name "hydra.error.packaging.InvalidModuleError.definitionsOutOfOrder"),
        (Core.Name "hydra.error.packaging.InvalidModuleError.duplicateDefinitionName"),
        (Core.Name "hydra.error.packaging.InvalidModuleError.invalidDefinitionName"),
        (Core.Name "hydra.error.packaging.InvalidModuleError.invalidModuleNameConvention"),
        (Core.Name "hydra.error.packaging.InvalidModuleError.missingDocumentation"),
        (Core.Name "hydra.error.packaging.InvalidPackageError.conflictingModuleName"),
        (Core.Name "hydra.error.packaging.InvalidPackageError.duplicateModuleName"),
        (Core.Name "hydra.error.packaging.InvalidPackageError.invalidPackageName")]),
      Validation.validationProfileWarningRules = Sets.empty,
      Validation.validationProfileMaxErrors = 1,
      Validation.validationProfileMaxWarnings = 20}
-- | Validate a kernel module against all kernel-default packaging rules; returns the first error found or nothing if valid. Convenience wrapper around 'module'' with 'kernelDefaultPackagingProfile'.
kernelModule :: Packaging.Module -> Maybe ErrorPackaging.InvalidModuleError
kernelModule mod =
    Lists.maybeHead (Validation.validationResultErrors (module_ kernelDefaultPackagingProfile (Validation.ValidationResult {
      Validation.validationResultErrors = [],
      Validation.validationResultWarnings = []}) mod))
-- | Validate a kernel package against all kernel-default packaging rules; returns the first error found or nothing if valid. Convenience wrapper around 'package' with 'kernelDefaultPackagingProfile'.
kernelPackage :: Packaging.Package -> Maybe ErrorPackaging.InvalidPackageError
kernelPackage pkg =
    Lists.maybeHead (Validation.validationResultErrors (package kernelDefaultPackagingProfile (Validation.ValidationResult {
      Validation.validationResultErrors = [],
      Validation.validationResultWarnings = []}) pkg))
-- | Validate a module against the given ValidationProfile, accumulating findings into a ValidationResult. Errors hard-stop the rule sequence once maxErrors is reached.
module_ :: Validation.ValidationProfile -> Validation.ValidationResult ErrorPackaging.InvalidModuleError -> Packaging.Module -> Validation.ValidationResult ErrorPackaging.InvalidModuleError
module_ p acc0 mod =
    Lists.foldl (\acc -> \guarded -> Logic.ifElse (Equality.gte (Lists.length (Validation.validationResultErrors acc)) (Validation.validationProfileMaxErrors p)) acc (appendFindingModule p acc guarded)) acc0 [
      Logic.ifElse (enabledPackaging p (Core.Name "hydra.error.packaging.InvalidModuleError.conflictingVariantName")) (Optionals.map (\f -> (Core.Name "hydra.error.packaging.InvalidModuleError.conflictingVariantName", f)) (checkConflictingVariantNames mod)) Nothing,
      (Logic.ifElse (enabledPackaging p (Core.Name "hydra.error.packaging.InvalidModuleError.missingDocumentation")) (Optionals.map (\f -> (Core.Name "hydra.error.packaging.InvalidModuleError.missingDocumentation", f)) (checkDefinitionDocumentation mod)) Nothing),
      (Logic.ifElse (enabledPackaging p (Core.Name "hydra.error.packaging.InvalidModuleError.invalidDefinitionName")) (Optionals.map (\f -> (Core.Name "hydra.error.packaging.InvalidModuleError.invalidDefinitionName", f)) (checkDefinitionNameConvention mod)) Nothing),
      (Logic.ifElse (enabledPackaging p (Core.Name "hydra.error.packaging.InvalidModuleError.definitionNotInModuleName")) (Optionals.map (\f -> (Core.Name "hydra.error.packaging.InvalidModuleError.definitionNotInModuleName", f)) (checkDefinitionModuleNames mod)) Nothing),
      (Logic.ifElse (enabledPackaging p (Core.Name "hydra.error.packaging.InvalidModuleError.definitionsOutOfOrder")) (Optionals.map (\f -> (Core.Name "hydra.error.packaging.InvalidModuleError.definitionsOutOfOrder", f)) (checkDefinitionOrdering mod)) Nothing),
      (Logic.ifElse (enabledPackaging p (Core.Name "hydra.error.packaging.InvalidModuleError.duplicateDefinitionName")) (Optionals.map (\f -> (Core.Name "hydra.error.packaging.InvalidModuleError.duplicateDefinitionName", f)) (checkDuplicateDefinitionNames mod)) Nothing),
      (Logic.ifElse (enabledPackaging p (Core.Name "hydra.error.packaging.InvalidModuleError.invalidModuleNameConvention")) (Optionals.map (\f -> (Core.Name "hydra.error.packaging.InvalidModuleError.invalidModuleNameConvention", f)) (checkModuleNameConvention mod)) Nothing)]
-- | Validate a package against the given ValidationProfile, accumulating findings into a ValidationResult. Errors hard-stop traversal once maxErrors is reached.
package :: Validation.ValidationProfile -> Validation.ValidationResult ErrorPackaging.InvalidPackageError -> Packaging.Package -> Validation.ValidationResult ErrorPackaging.InvalidPackageError
package p acc0 pkg =

      let accPkg =
              Lists.foldl (\acc -> \guarded -> Logic.ifElse (Equality.gte (Lists.length (Validation.validationResultErrors acc)) (Validation.validationProfileMaxErrors p)) acc (appendFindingPackage p acc guarded)) acc0 [
                Logic.ifElse (enabledPackaging p (Core.Name "hydra.error.packaging.InvalidPackageError.conflictingModuleName")) (Optionals.map (\f -> (Core.Name "hydra.error.packaging.InvalidPackageError.conflictingModuleName", f)) (checkConflictingModuleNames pkg)) Nothing,
                (Logic.ifElse (enabledPackaging p (Core.Name "hydra.error.packaging.InvalidPackageError.duplicateModuleName")) (Optionals.map (\f -> (Core.Name "hydra.error.packaging.InvalidPackageError.duplicateModuleName", f)) (checkDuplicateModuleNames pkg)) Nothing),
                (Logic.ifElse (enabledPackaging p (Core.Name "hydra.error.packaging.InvalidPackageError.invalidPackageName")) (Optionals.map (\f -> (Core.Name "hydra.error.packaging.InvalidPackageError.invalidPackageName", f)) (checkPackageNameConvention pkg)) Nothing)]
      in (Lists.foldl (\acc -> \mod -> Logic.ifElse (Equality.gte (Lists.length (Validation.validationResultErrors acc)) (Validation.validationProfileMaxErrors p)) acc (
        let mr =
                module_ p (Validation.ValidationResult {
                  Validation.validationResultErrors = [],
                  Validation.validationResultWarnings = []}) mod
            liftedErrs = Lists.map (\e -> ErrorPackaging.InvalidPackageErrorInvalidModule e) (Validation.validationResultErrors mr)
            liftedWrns = Lists.map (\w -> ErrorPackaging.InvalidPackageErrorInvalidModule w) (Validation.validationResultWarnings mr)
            newErrs =
                    Lists.take (Validation.validationProfileMaxErrors p) (Lists.concat2 (Validation.validationResultErrors acc) liftedErrs)
            newWrns =
                    Lists.take (Validation.validationProfileMaxWarnings p) (Lists.concat2 (Validation.validationResultWarnings acc) liftedWrns)
        in Validation.ValidationResult {
          Validation.validationResultErrors = newErrs,
          Validation.validationResultWarnings = newWrns})) accPkg (Packaging.packageModules pkg))