{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Spec.Parser
( readInputExpr
, readInputFile
)
where
import qualified Control.Exception as E
import Control.Monad.Except (ExceptT (..))
import Data.Aeson (eitherDecode)
import qualified Data.ByteString.Lazy as L
import Data.List (isInfixOf, isPrefixOf, nub, (\\))
import System.Directory (doesFileExist)
import System.FilePath ((</>))
import System.Process (readProcess)
import Data.ByteString.Extra as B (safeReadFile)
import Data.String.Extra (sanitizeLCIdentifier, sanitizeUCIdentifier)
import Data.OgmaSpec (ExternalVariableDef (..),
InternalVariableDef (..), Requirement (..),
Spec (..))
import Language.CSVSpec.Parser (parseCSVSpec)
import Language.JSONSpec.Parser (parseJSONSpec)
import Language.XLSXSpec.Parser (parseXLSXSpec)
import Language.XMLSpec.Parser (parseXMLSpec)
import Language.YAMLSpec.Parser (parseYAMLSpec)
import Command.Errors (ErrorTriplet(..), ErrorCode)
import Data.Diagram (Diagram)
import Data.Either.Extra (mapLeft)
import Data.ExprPair (ExprPairT(..))
import Data.Location (Location (..))
import Paths_ogma_core (getDataDir)
readInputExpr :: String
-> String
-> Maybe String
-> ExprPairT a
-> ExceptT ErrorTriplet IO (Spec a)
readInputExpr :: forall a.
String
-> String
-> Maybe String
-> ExprPairT a
-> ExceptT ErrorTriplet IO (Spec a)
readInputExpr String
expr String
propFormatName Maybe String
propVia ExprPairT a
exprT =
IO (Either ErrorTriplet (Spec a))
-> ExceptT ErrorTriplet IO (Spec a)
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (IO (Either ErrorTriplet (Spec a))
-> ExceptT ErrorTriplet IO (Spec a))
-> IO (Either ErrorTriplet (Spec a))
-> ExceptT ErrorTriplet IO (Spec a)
forall a b. (a -> b) -> a -> b
$ do
let ExprPairT String -> Either String a
parse [(String, String)] -> a -> a
replace a -> String
print a -> [String]
ids a
def = ExprPairT a
exprT
let wrapper :: String -> IO (Either String a)
wrapper = Maybe String
-> (String -> Either String a) -> String -> IO (Either String a)
forall a.
Maybe String
-> (String -> Either String a) -> String -> IO (Either String a)
wrapVia Maybe String
propVia String -> Either String a
parse
result <- String -> IO (Either String a)
wrapper String
expr
let spec = do
expr' <- Either String a
result
let req = String -> a -> String -> Maybe String -> Maybe a -> Requirement a
forall a.
String -> a -> String -> Maybe String -> Maybe a -> Requirement a
Requirement String
"triggerCondition" a
expr' String
"" Maybe String
forall a. Maybe a
Nothing Maybe a
forall a. Maybe a
Nothing
return $ Spec [] [] [ req ]
pure $ mapLeft (cannotReadConditionExpr expr) spec
readInputFile :: FilePath
-> String
-> String
-> Maybe String
-> ExprPairT a
-> ExceptT ErrorTriplet IO (Spec a)
readInputFile :: forall a.
String
-> String
-> String
-> Maybe String
-> ExprPairT a
-> ExceptT ErrorTriplet IO (Spec a)
readInputFile String
fp String
formatName String
propFormatName Maybe String
propVia ExprPairT a
exprT =
IO (Either ErrorTriplet (Spec a))
-> ExceptT ErrorTriplet IO (Spec a)
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (IO (Either ErrorTriplet (Spec a))
-> ExceptT ErrorTriplet IO (Spec a))
-> IO (Either ErrorTriplet (Spec a))
-> ExceptT ErrorTriplet IO (Spec a)
forall a b. (a -> b) -> a -> b
$ do
let ExprPairT String -> Either String a
parse [(String, String)] -> a -> a
replace a -> String
print a -> [String]
ids a
def = ExprPairT a
exprT
let wrapper :: String -> IO (Either String a)
wrapper = Maybe String
-> (String -> Either String a) -> String -> IO (Either String a)
forall a.
Maybe String
-> (String -> Either String a) -> String -> IO (Either String a)
wrapVia Maybe String
propVia String -> Either String a
parse
exists <- String -> IO Bool
doesFileExist String
formatName
dataDir <- getDataDir
let formatFile
| String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isInfixOf String
"/" String
formatName Bool -> Bool -> Bool
|| Bool
exists
= String
formatName
| Bool
otherwise
= String
dataDir String -> String -> String
</> String
"data" String -> String -> String
</> String
"formats" String -> String -> String
</>
(String
formatName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"_" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
propFormatName)
formatMissing <- not <$> doesFileExist formatFile
if formatMissing
then return $ Left $ commandIncorrectFormatSpec formatFile
else do
res <- do
format <- readFile formatFile
if | isPrefixOf "XMLFormat" format
-> do let xmlFormat = String -> XMLFormat
forall a. Read a => String -> a
read String
format
content <- readFile fp
parseXMLSpec
(wrapper) (def) xmlFormat content
| isPrefixOf "CSVFormat" format
-> do let csvFormat = String -> CSVFormat
forall a. Read a => String -> a
read String
format
content <- readFile fp
parseCSVSpec wrapper def csvFormat content
| isPrefixOf "XLSXFormat" format
-> do let xlsxFormat = String -> XLSXFormat
forall a. Read a => String -> a
read String
format
content <- L.readFile fp
parseXLSXSpec wrapper def xlsxFormat content
| isPrefixOf "YAMLFormat" format
-> do let yamlFormat = String -> YAMLFormat
forall a. Read a => String -> a
read String
format
content <- B.safeReadFile fp
case content of
Left String
e -> Either String (Spec a) -> IO (Either String (Spec a))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String (Spec a) -> IO (Either String (Spec a)))
-> Either String (Spec a) -> IO (Either String (Spec a))
forall a b. (a -> b) -> a -> b
$ String -> Either String (Spec a)
forall a b. a -> Either a b
Left String
e
Right ByteString
b -> (String -> IO (Either String a))
-> YAMLFormat -> ByteString -> IO (Either String (Spec a))
forall a.
(String -> IO (Either String a))
-> YAMLFormat -> ByteString -> IO (Either String (Spec a))
parseYAMLSpec String -> IO (Either String a)
wrapper YAMLFormat
yamlFormat (ByteString -> ByteString
L.toStrict ByteString
b)
| otherwise
-> do let jsonFormat = String -> JSONFormat
forall a. Read a => String -> a
read String
format
content <- B.safeReadFile fp
case content of
Left String
e -> Either String (Spec a) -> IO (Either String (Spec a))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String (Spec a) -> IO (Either String (Spec a)))
-> Either String (Spec a) -> IO (Either String (Spec a))
forall a b. (a -> b) -> a -> b
$ String -> Either String (Spec a)
forall a b. a -> Either a b
Left String
e
Right ByteString
b -> do case ByteString -> Either String Value
forall a. FromJSON a => ByteString -> Either String a
eitherDecode ByteString
b of
Left String
e -> Either String (Spec a) -> IO (Either String (Spec a))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String (Spec a) -> IO (Either String (Spec a)))
-> Either String (Spec a) -> IO (Either String (Spec a))
forall a b. (a -> b) -> a -> b
$ String -> Either String (Spec a)
forall a b. a -> Either a b
Left String
e
Right Value
v ->
(String -> IO (Either String a))
-> JSONFormat -> Value -> IO (Either String (Spec a))
forall a.
(String -> IO (Either String a))
-> JSONFormat -> Value -> IO (Either String (Spec a))
parseJSONSpec
(String -> IO (Either String a)
wrapper)
JSONFormat
jsonFormat
Value
v
case res of
Left String
e -> Either ErrorTriplet (Spec a) -> IO (Either ErrorTriplet (Spec a))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either ErrorTriplet (Spec a) -> IO (Either ErrorTriplet (Spec a)))
-> Either ErrorTriplet (Spec a)
-> IO (Either ErrorTriplet (Spec a))
forall a b. (a -> b) -> a -> b
$ ErrorTriplet -> Either ErrorTriplet (Spec a)
forall a b. a -> Either a b
Left (ErrorTriplet -> Either ErrorTriplet (Spec a))
-> ErrorTriplet -> Either ErrorTriplet (Spec a)
forall a b. (a -> b) -> a -> b
$ String -> ErrorTriplet
cannotOpenInputFile String
fp
Right Spec a
x -> Either ErrorTriplet (Spec a) -> IO (Either ErrorTriplet (Spec a))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either ErrorTriplet (Spec a) -> IO (Either ErrorTriplet (Spec a)))
-> Either ErrorTriplet (Spec a)
-> IO (Either ErrorTriplet (Spec a))
forall a b. (a -> b) -> a -> b
$ Spec a -> Either ErrorTriplet (Spec a)
forall a b. b -> Either a b
Right Spec a
x
cannotReadConditionExpr :: String -> String -> ErrorTriplet
cannotReadConditionExpr :: String -> String -> ErrorTriplet
cannotReadConditionExpr String
expr String
errorMsg =
ErrorCode -> String -> Location -> ErrorTriplet
ErrorTriplet ErrorCode
ecCannotReadConditionExpr String
msg Location
LocationNothing
where
msg :: String
msg =
String
"cannot parse condition or trigger expression " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
forall a. Show a => a -> String
show String
expr String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
":"
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
errorMsg
cannotOpenInputFile :: FilePath -> ErrorTriplet
cannotOpenInputFile :: String -> ErrorTriplet
cannotOpenInputFile String
file =
ErrorCode -> String -> Location -> ErrorTriplet
ErrorTriplet ErrorCode
ecCannotOpenInputFile String
msg (String -> Location
LocationFile String
file)
where
msg :: String
msg =
String
"cannot open input specification file " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
file
commandIncorrectFormatSpec :: FilePath -> ErrorTriplet
commandIncorrectFormatSpec :: String -> ErrorTriplet
commandIncorrectFormatSpec String
formatFile =
ErrorCode -> String -> Location -> ErrorTriplet
ErrorTriplet ErrorCode
ecIncorrectFormatFile String
msg (String -> Location
LocationFile String
formatFile)
where
msg :: String
msg =
String
"The format specification " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
formatFile String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" does not exist or is not "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"readable"
ecCannotReadConditionExpr :: ErrorCode
ecCannotReadConditionExpr :: ErrorCode
ecCannotReadConditionExpr = ErrorCode
1
ecCannotOpenInputFile :: ErrorCode
ecCannotOpenInputFile :: ErrorCode
ecCannotOpenInputFile = ErrorCode
1
ecIncorrectFormatFile :: ErrorCode
ecIncorrectFormatFile :: ErrorCode
ecIncorrectFormatFile = ErrorCode
1
wrapVia :: Maybe String
-> (String -> Either String a)
-> String
-> IO (Either String a)
wrapVia :: forall a.
Maybe String
-> (String -> Either String a) -> String -> IO (Either String a)
wrapVia Maybe String
Nothing String -> Either String a
parse String
s = Either String a -> IO (Either String a)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String a
parse String
s)
wrapVia (Just String
f) String -> Either String a
parse String
s =
(IOException -> IO (Either String a))
-> IO (Either String a) -> IO (Either String a)
forall e a. Exception e => (e -> IO a) -> IO a -> IO a
E.handle (\(IOException
e :: E.IOException) -> Either String a -> IO (Either String a)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String a -> IO (Either String a))
-> Either String a -> IO (Either String a)
forall a b. (a -> b) -> a -> b
$ String -> Either String a
forall a b. a -> Either a b
Left (String -> Either String a) -> String -> Either String a
forall a b. (a -> b) -> a -> b
$ IOException -> String
forall a. Show a => a -> String
show IOException
e) (IO (Either String a) -> IO (Either String a))
-> IO (Either String a) -> IO (Either String a)
forall a b. (a -> b) -> a -> b
$ do
out <- String -> [String] -> String -> IO String
readProcess String
f [] String
s
return $ parse out