packages feed

ogma-language-jsonspec 1.14.0 → 1.15.0

raw patch · 3 files changed

+156/−41 lines, 3 filesdep +filepathdep ~ogma-specPVP ok

version bump matches the API change (PVP)

Dependencies added: filepath

Dependency ranges changed: ogma-spec

API changes (from Hackage documentation)

+ Language.JSONSpec.Parser: BaseName :: FieldSource
+ Language.JSONSpec.Parser: FSIBaseName :: FieldSourceInternal
+ Language.JSONSpec.Parser: FSIFileName :: FieldSourceInternal
+ Language.JSONSpec.Parser: FSIJSONPath :: [JSONPathElement] -> FieldSourceInternal
+ Language.JSONSpec.Parser: FileName :: FieldSource
+ Language.JSONSpec.Parser: JSONPath :: String -> FieldSource
+ Language.JSONSpec.Parser: data FieldSource
+ Language.JSONSpec.Parser: data FieldSourceInternal
+ Language.JSONSpec.Parser: instance GHC.Read.Read Language.JSONSpec.Parser.FieldSource
+ Language.JSONSpec.Parser: instance GHC.Show.Show Language.JSONSpec.Parser.FieldSource
+ Language.JSONSpec.Parser: instance GHC.Show.Show Language.JSONSpec.Parser.FieldSourceInternal
- Language.JSONSpec.Parser: JSONFormat :: Maybe String -> String -> String -> Maybe String -> Maybe String -> String -> Maybe String -> String -> String -> Maybe String -> String -> Maybe String -> Maybe String -> JSONFormat
+ Language.JSONSpec.Parser: JSONFormat :: Maybe String -> String -> String -> Maybe String -> Maybe String -> String -> Maybe String -> String -> FieldSource -> Maybe String -> String -> Maybe String -> Maybe String -> JSONFormat
- Language.JSONSpec.Parser: JSONFormatInternal :: Maybe [JSONPathElement] -> [JSONPathElement] -> [JSONPathElement] -> Maybe [JSONPathElement] -> Maybe [JSONPathElement] -> [JSONPathElement] -> Maybe [JSONPathElement] -> [JSONPathElement] -> [JSONPathElement] -> Maybe [JSONPathElement] -> [JSONPathElement] -> Maybe [JSONPathElement] -> Maybe [JSONPathElement] -> JSONFormatInternal
+ Language.JSONSpec.Parser: JSONFormatInternal :: Maybe [JSONPathElement] -> [JSONPathElement] -> [JSONPathElement] -> Maybe [JSONPathElement] -> Maybe [JSONPathElement] -> [JSONPathElement] -> Maybe [JSONPathElement] -> [JSONPathElement] -> FieldSourceInternal -> Maybe [JSONPathElement] -> [JSONPathElement] -> Maybe [JSONPathElement] -> Maybe [JSONPathElement] -> JSONFormatInternal
- Language.JSONSpec.Parser: [jfiRequirementId] :: JSONFormatInternal -> [JSONPathElement]
+ Language.JSONSpec.Parser: [jfiRequirementId] :: JSONFormatInternal -> FieldSourceInternal
- Language.JSONSpec.Parser: [specRequirementId] :: JSONFormat -> String
+ Language.JSONSpec.Parser: [specRequirementId] :: JSONFormat -> FieldSource
- Language.JSONSpec.Parser: parseJSONSpec :: (String -> IO (Either String a)) -> JSONFormat -> Value -> IO (Either String (Spec a))
+ Language.JSONSpec.Parser: parseJSONSpec :: (String -> IO (Either String a)) -> JSONFormat -> FilePath -> Value -> IO (Either String (Spec a))

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for ogma-language-jsonspec +## [1.15.0] - 2026-07-21++* Version bump (1.15.0) (#508).+* Allow using file name as requirement ID in JSON files (#472).+* Apply multiple style fixes (#492).+ ## [1.14.0] - 2026-05-21  * Version bump (1.14.0) (#425).
ogma-language-jsonspec.cabal view
@@ -19,7 +19,7 @@ build-type:          Simple  name:                ogma-language-jsonspec-version:             1.14.0+version:             1.15.0 homepage:            https://github.com/nasa/ogma bug-reports:         https://github.com/nasa/ogma/issues license:             Apache-2.0@@ -58,13 +58,14 @@   build-depends:       base                   >= 4.11.0.0 && < 5     , aeson                  >= 2.0.0.0  && < 2.3+    , filepath               >= 1.4.2    && < 1.6     , jsonpath               >= 0.3      && < 0.4     , text                   >= 1.2.3.1  && < 2.2     , megaparsec             >= 8.0.0    && < 9.8     , mtl                    >= 2.2.2    && < 2.4     , bytestring             >= 0.10.8.2 && < 0.13 -    , ogma-spec              >= 1.14.0 && < 1.15+    , ogma-spec              >= 1.15.0 && < 1.16    hs-source-dirs:     src
src/Language/JSONSpec/Parser.hs view
@@ -27,17 +27,19 @@ import           Data.Aeson.Types      (prependFailure, typeMismatch) import           Data.Bifunctor        (first) import           Data.ByteString.Lazy  (fromStrict)-import           Data.JSONPath.Execute-import           Data.JSONPath.Parser-import           Data.JSONPath.Types+import           Data.JSONPath.Execute (executeJSONPath)+import           Data.JSONPath.Parser  (jsonPath)+import           Data.JSONPath.Types   (JSONPathElement(..)) import           Data.Text             (pack, unpack) import qualified Data.Text             as T import qualified Data.Text.Encoding    as T import qualified Data.Text.IO          as T+import           System.FilePath       (takeBaseName, takeFileName) import           Text.Megaparsec       (eof, errorBundlePretty, parse)  -- External imports: ogma-spec-import Data.OgmaSpec+import Data.OgmaSpec (ExternalVariableDef (..), InternalVariableDef (..),+                      Requirement (..), Spec (..))  data JSONFormat = JSONFormat     { specInternalVars          :: Maybe String@@ -48,7 +50,7 @@     , specExternalVarId         :: String     , specExternalVarType       :: Maybe String     , specRequirements          :: String-    , specRequirementId         :: String+    , specRequirementId         :: FieldSource     , specRequirementDesc       :: Maybe String     , specRequirementExpr       :: String     , specRequirementResultType :: Maybe String@@ -56,6 +58,25 @@     }   deriving (Read) +-- | Source used to populate the value of a field in a spec.+data FieldSource+    = JSONPath String -- ^ JSON path+    | FileName        -- ^ Filename with extension+    | BaseName        -- ^ Filename without extension+  deriving (Show)++-- | Custom instance to read a 'FieldSource' that allows JSON paths to be+-- written down as plain strings.+instance Read FieldSource where+  readsPrec prec str =+    case lex str of+      [("JSONPath", rest)] -> first JSONPath <$> readsPrec prec rest+      [("FileName", rest)] -> [(FileName, rest)]+      [("BaseName", rest)] -> [(BaseName, rest)]+      -- If it doesn't match a constructor, we attempt to read a string and+      -- treat it as a JSONPath.+      _                    -> first JSONPath <$> readsPrec prec str+ data JSONFormatInternal = JSONFormatInternal   { jfiInternalVars          :: Maybe [JSONPathElement]   , jfiInternalVarId         :: [JSONPathElement]@@ -65,28 +86,55 @@   , jfiExternalVarId         :: [JSONPathElement]   , jfiExternalVarType       :: Maybe [JSONPathElement]   , jfiRequirements          :: [JSONPathElement]-  , jfiRequirementId         :: [JSONPathElement]+  , jfiRequirementId         :: FieldSourceInternal   , jfiRequirementDesc       :: Maybe [JSONPathElement]   , jfiRequirementExpr       :: [JSONPathElement]   , jfiRequirementResultType :: Maybe [JSONPathElement]   , jfiRequirementResultExpr :: Maybe [JSONPathElement]   } +-- | Internal representation of the source used to populate the value of a+-- field in a spec.+data FieldSourceInternal+    = FSIJSONPath [JSONPathElement] -- ^ JSON path+    | FSIFileName                   -- ^ Filename with extension+    | FSIBaseName                   -- ^ Filename without extension+  deriving (Show)+ parseJSONFormat :: JSONFormat -> Either String JSONFormatInternal parseJSONFormat jsonFormat = do-  jfi2  <- showErrorsM $ fmap (parseJSONPath . pack) $ specInternalVars    jsonFormat-  jfi3  <- showErrors $ parseJSONPath $ pack $ specInternalVarId   jsonFormat-  jfi4  <- showErrors $ parseJSONPath $ pack $ specInternalVarExpr jsonFormat-  jfi5  <- showErrorsM $ fmap (parseJSONPath . pack) $ specInternalVarType jsonFormat-  jfi6  <- showErrorsM $ fmap (parseJSONPath . pack) $ specExternalVars    jsonFormat-  jfi7  <- showErrors $ parseJSONPath $ pack $ specExternalVarId   jsonFormat-  jfi8  <- showErrorsM $ fmap (parseJSONPath . pack) $ specExternalVarType jsonFormat-  jfi9  <- showErrors $ parseJSONPath $ pack $ specRequirements    jsonFormat-  jfi10 <- showErrors $ parseJSONPath $ pack $ specRequirementId   jsonFormat-  jfi11 <- showErrorsM $ fmap (parseJSONPath . pack) $ specRequirementDesc jsonFormat-  jfi12 <- showErrors $ parseJSONPath $ pack $ specRequirementExpr jsonFormat-  jfi13 <- showErrorsM $ fmap (parseJSONPath . pack) $ specRequirementResultType jsonFormat-  jfi14 <- showErrorsM $ fmap (parseJSONPath . pack) $ specRequirementResultExpr jsonFormat+  jfi2 <- showErrorsM $+            fmap (parseJSONPath . pack) $ specInternalVars jsonFormat+  jfi3 <- showErrors $+            parseJSONPath $ pack $ specInternalVarId jsonFormat+  jfi4 <- showErrors $+            parseJSONPath $ pack $ specInternalVarExpr jsonFormat+  jfi5 <- showErrorsM $+            fmap (parseJSONPath . pack) $ specInternalVarType jsonFormat+  jfi6 <- showErrorsM $+            fmap (parseJSONPath . pack) $ specExternalVars jsonFormat+  jfi7 <- showErrors $+            parseJSONPath $ pack $ specExternalVarId jsonFormat+  jfi8 <- showErrorsM $+            fmap (parseJSONPath . pack) $ specExternalVarType jsonFormat+  jfi9 <- showErrors $+            parseJSONPath $ pack $ specRequirements jsonFormat++  -- Handle the case where the requirement ID is the file name, with or without+  -- extension.+  jfi10 <- case specRequirementId jsonFormat of+    FileName   -> return FSIFileName+    BaseName   -> return FSIBaseName+    JSONPath p -> showErrors $ fmap FSIJSONPath $ parseJSONPath $ pack p++  jfi11 <- showErrorsM $+             fmap (parseJSONPath . pack) $ specRequirementDesc jsonFormat+  jfi12 <- showErrors $+             parseJSONPath $ pack $ specRequirementExpr jsonFormat+  jfi13 <- showErrorsM $+             fmap (parseJSONPath . pack) $ specRequirementResultType jsonFormat+  jfi14 <- showErrorsM $+             fmap (parseJSONPath . pack) $ specRequirementResultExpr jsonFormat   return $ JSONFormatInternal              { jfiInternalVars          = jfi2              , jfiInternalVarId         = jfi3@@ -103,47 +151,82 @@              , jfiRequirementResultExpr = jfi14              } -parseJSONSpec :: (String -> IO (Either String a)) -> JSONFormat -> Value -> IO (Either String (Spec a))-parseJSONSpec parseExpr jsonFormat value = runExceptT $ do+parseJSONSpec :: (String -> IO (Either String a))+              -> JSONFormat+              -> FilePath+              -> Value+              -> IO (Either String (Spec a))+parseJSONSpec parseExpr jsonFormat filepath value = runExceptT $ do   jsonFormatInternal <- except $ parseJSONFormat jsonFormat    let values :: [Value]-      values = maybe [] (`executeJSONPath` value) (jfiInternalVars jsonFormatInternal)+      values =+        maybe [] (`executeJSONPath` value) (jfiInternalVars jsonFormatInternal)        internalVarDef :: Value -> Either String InternalVariableDef       internalVarDef value = do         let msg = "internal variable name"-        varId   <- valueToString msg =<< (listToEither msg (executeJSONPath (jfiInternalVarId jsonFormatInternal) value))+        varId <- valueToString msg =<<+                   listToEither+                     msg+                     ( executeJSONPath+                         (jfiInternalVarId jsonFormatInternal)+                         value+                     )          let msg = "internal variable type"-        varType <- maybe (Right "") (\e -> valueToString msg =<< (listToEither msg (executeJSONPath e value))) (jfiInternalVarType jsonFormatInternal)+        varType <- maybe+                     (Right "")+                     (\e -> valueToString msg =<<+                              listToEither msg (executeJSONPath e value)+                     )+                     (jfiInternalVarType jsonFormatInternal)          let msg = "internal variable expr"-        varExpr <- valueToString msg =<< (listToEither msg (executeJSONPath (jfiInternalVarExpr jsonFormatInternal) value))+        varExpr <- valueToString msg =<<+                     listToEither+                       msg+                       ( executeJSONPath+                           (jfiInternalVarExpr jsonFormatInternal)+                           value+                       )          return $ InternalVariableDef-                   { internalVariableName    = varId-                   , internalVariableType    = varType-                   , internalVariableExpr    = varExpr+                   { internalVariableName = varId+                   , internalVariableType = varType+                   , internalVariableExpr = varExpr                    }    internalVariableDefs <- except $ mapM internalVarDef values    let values :: [Value]-      values = maybe [] (`executeJSONPath` value) (jfiExternalVars jsonFormatInternal)+      values =+        maybe [] (`executeJSONPath` value) (jfiExternalVars jsonFormatInternal)        externalVarDef :: Value -> Either String ExternalVariableDef       externalVarDef value = do          let msg = "external variable name"-        varId   <- valueToString msg =<< (listToEither msg (executeJSONPath (jfiExternalVarId jsonFormatInternal) value))+        varId <- valueToString msg =<<+                   listToEither+                     msg+                     ( executeJSONPath+                         (jfiExternalVarId jsonFormatInternal)+                         value+                     )          let msg = "external variable type"-        varType <- maybe (Right "") (\e -> valueToString msg =<< (listToEither msg (executeJSONPath e value))) (jfiExternalVarType jsonFormatInternal)+        varType <-+          maybe+            (Right "")+            (\e -> valueToString msg =<<+                     listToEither msg (executeJSONPath e value)+            )+            (jfiExternalVarType jsonFormatInternal)          return $ ExternalVariableDef-                   { externalVariableName    = varId-                   , externalVariableType    = varType+                   { externalVariableName = varId+                   , externalVariableType = varType                    }    externalVariableDefs <- except $ mapM externalVarDef values@@ -154,23 +237,47 @@       -- requirementDef :: Value -> Either String (Requirement a)       requirementDef value = do         let msg = "Requirement name"-        reqId <- except $ valueToString msg =<< (listToEither msg (executeJSONPath (jfiRequirementId jsonFormatInternal) value)) +        -- Handle the case where the requirement ID is the file name, with or+        -- without extension.+        reqId <- case jfiRequirementId jsonFormatInternal of+          FSIFileName   -> return $ takeFileName filepath+          FSIBaseName   -> return $ takeBaseName filepath+          FSIJSONPath p -> except $+            valueToString msg =<< listToEither msg (executeJSONPath p value)+         let msg = "Requirement expression"-        reqExpr <- except $ valueToString msg =<< (listToEither msg (executeJSONPath (jfiRequirementExpr jsonFormatInternal) value))+        reqExpr <- except $ valueToString msg =<<+                              listToEither+                                msg+                                ( executeJSONPath+                                    (jfiRequirementExpr jsonFormatInternal)+                                    value+                                )         reqExpr' <- ExceptT $ parseExpr reqExpr          let msg = "Requirement description"-        reqDesc <- except $ maybe (Right "") (\e -> valueToString msg =<< (listToEither msg (executeJSONPath e value))) (jfiRequirementDesc jsonFormatInternal)+        reqDesc <- except $ maybe+                     (Right "")+                     (\e -> valueToString msg =<<+                              listToEither msg (executeJSONPath e value)+                     )+                     (jfiRequirementDesc jsonFormatInternal)          let msg = "Requirement result type"             ty :: Maybe (Either String String)-            ty = (\e -> valueToString msg =<< (listToEither msg (executeJSONPath e value))) <$> (jfiRequirementResultType jsonFormatInternal)+            ty = (\e -> valueToString msg =<<+                          listToEither msg (executeJSONPath e value)+                 )+             <$> jfiRequirementResultType jsonFormatInternal         reqResType <- except $ maybeEither ty          let msg = "Requirement result expression"             resultExpr :: Maybe (Either String String)-            resultExpr = (\e -> valueToString msg =<< (listToEither msg (executeJSONPath e value))) <$> (jfiRequirementResultExpr jsonFormatInternal)+            resultExpr = (\e -> valueToString msg =<<+                                  listToEither msg (executeJSONPath e value)+                         )+                     <$> jfiRequirementResultExpr jsonFormatInternal          reqResExpr  <- except $ maybeEither resultExpr         reqResExpr' <- ExceptT $ case reqResExpr of@@ -191,7 +298,8 @@  valueToString :: String -> Value -> Either String String valueToString msg (String x) = Right $ unpack x-valueToString msg _          = Left $ "The JSON value provided for " ++ msg ++ " does not contain a string"+valueToString msg _          = Left $+  "The JSON value provided for " ++ msg ++ " does not contain a string"  listToEither :: String -> [a] -> Either String a listToEither _   [x] = Right x