    {-# LANGUAGE ExistentialQuantification #-}
    {-# LANGUAGE MultiWayIf                #-}
    {-# LANGUAGE OverloadedStrings         #-}
    {-# LANGUAGE ScopedTypeVariables       #-}
    -- Copyright 2022 United States Government as represented by the Administrator
    -- of the National Aeronautics and Space Administration. All Rights Reserved.
    --
    -- Disclaimers
    --
    -- Licensed under the Apache License, Version 2.0 (the "License"); you may
    -- not use this file except in compliance with the License. You may obtain a
    -- copy of the License at
    --
    --      https://www.apache.org/licenses/LICENSE-2.0
    --
    -- Unless required by applicable law or agreed to in writing, software
    -- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    -- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    -- License for the specific language governing permissions and limitations
    -- under the License.
    --
    -- | Shared functions across multiple backends.
    module Command.Common
        ( InputFile(..)
        , combineInputFiles
        , parseInputFile
        , parseVariablesFile
        , parseRequirementsListFile
        , openVarDBFiles
        , openVarDBFilesWithDefault
        , parseTemplateVarsFile
        , checkArguments
        , specExtractExternalVariables
        , specExtractHandlers
        , processResult
        , cannotCopyTemplate
        , makeLeftE
        , locateTemplateDir
        )
      where

    -- External imports
    import qualified Control.Exception      as E
    import           Control.Monad.Except   (ExceptT (..), runExceptT, throwError)
    import           Control.Monad.IO.Class (liftIO)
    import           Data.Aeson             (Value (Null, Object), eitherDecode,
                                             eitherDecodeFileStrict, object)
    import           System.FilePath        ((</>))

    -- External imports: auxiliary
    import Data.ByteString.Extra as B (safeReadFile)
    import Data.String.Extra     (sanitizeLCIdentifier, sanitizeUCIdentifier)

    -- External imports: ogma
    import Data.OgmaSpec (Requirement (..), Spec (..), externalVariableName,
                          externalVariables, requirementName, requirementResultType,
                          requirements)

    -- Internal imports: VariableDBs
    import Command.VariableDB (VariableDB, emptyVariableDB, mergeVariableDB)

    -- Internal imports: auxiliary
    import Command.Errors      (ErrorTriplet(..), ErrorCode)
    import Command.Result      (Result (..))
    import Data.Diagram        (Diagram)
    import Data.Diagram.Parser (DiagramFormat (..), readDiagram)
    import Data.Either.Extra   (makeLeft)
    import Data.ExprPair       (ExprPair (..), ExprPairT (..))
    import Data.Location       (Location (..))
    import Data.Spec.Parser    (readInputFile)
    import Paths_ogma_core     (getDataDir)

    -- | File containing information to be processed by Ogma (e.g., specification,
    -- diagram).
    data InputFile a = InputFileDiagram Diagram
                     | InputFileSpec    (Spec a)

    -- | Merge a list of input files into a smaller list of input files.
    --
    -- PRE: If there is more than one input file, all input files are specs.
    combineInputFiles :: [InputFile a] -> [InputFile a]
    combineInputFiles :: forall a. [InputFile a] -> [InputFile a]
    combineInputFiles []  = []
    combineInputFiles [InputFile a
    x] = [InputFile a
    x]
    combineInputFiles [InputFile a]
    xs
        | (InputFile a -> Bool) -> [InputFile a] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any InputFile a -> Bool
    forall a. InputFile a -> Bool
    isInputDiagram [InputFile a]
    xs
        = []

        | Bool
    otherwise
        = [ Spec a -> InputFile a
    forall a. Spec a -> InputFile a
    InputFileSpec (Spec a -> InputFile a) -> Spec a -> InputFile a
    forall a b. (a -> b) -> a -> b
    $ (Spec a -> Spec a -> Spec a) -> [Spec a] -> Spec a
    forall a. (a -> a -> a) -> [a] -> a
    forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
    foldr1 Spec a -> Spec a -> Spec a
    forall a. Spec a -> Spec a -> Spec a
    mergeSpecs ([Spec a] -> Spec a) -> [Spec a] -> Spec a
    forall a b. (a -> b) -> a -> b
    $ (InputFile a -> Spec a) -> [InputFile a] -> [Spec a]
    forall a b. (a -> b) -> [a] -> [b]
    map InputFile a -> Spec a
    forall a. InputFile a -> Spec a
    getSpec [InputFile a]
    xs ]

      where

        -- True if the given argument is a diagram.
        isInputDiagram :: InputFile a -> Bool
        isInputDiagram :: forall a. InputFile a -> Bool
    isInputDiagram (InputFileDiagram Diagram
    _) = Bool
    True
        isInputDiagram InputFile a
    _                    = Bool
    False

        -- Merge two specifications.
        mergeSpecs :: Spec a -> Spec a -> Spec a
        mergeSpecs :: forall a. Spec a -> Spec a -> Spec a
    mergeSpecs Spec a
    s1 Spec a
    s2 = Spec
          { internalVariables :: [InternalVariableDef]
    internalVariables = Spec a -> [InternalVariableDef]
    forall a. Spec a -> [InternalVariableDef]
    internalVariables Spec a
    s1 [InternalVariableDef]
    -> [InternalVariableDef] -> [InternalVariableDef]
    forall a. [a] -> [a] -> [a]
    ++ Spec a -> [InternalVariableDef]
    forall a. Spec a -> [InternalVariableDef]
    internalVariables Spec a
    s2
          , externalVariables :: [ExternalVariableDef]
    externalVariables = Spec a -> [ExternalVariableDef]
    forall a. Spec a -> [ExternalVariableDef]
    externalVariables Spec a
    s2 [ExternalVariableDef]
    -> [ExternalVariableDef] -> [ExternalVariableDef]
    forall a. [a] -> [a] -> [a]
    ++ Spec a -> [ExternalVariableDef]
    forall a. Spec a -> [ExternalVariableDef]
    externalVariables Spec a
    s2
          , requirements :: [Requirement a]
    requirements      = Spec a -> [Requirement a]
    forall a. Spec a -> [Requirement a]
    requirements Spec a
    s1 [Requirement a] -> [Requirement a] -> [Requirement a]
    forall a. [a] -> [a] -> [a]
    ++ Spec a -> [Requirement a]
    forall a. Spec a -> [Requirement a]
    requirements Spec a
    s2
          }

        -- Unsafely unwrap the spec in an input file.
        --
        -- PRE: The argument input file contains a spec.
        getSpec :: InputFile a -> Spec a
        getSpec :: forall a. InputFile a -> Spec a
    getSpec (InputFileSpec Spec a
    s) = Spec a
    s
        getSpec InputFile a
    _                 = [Char] -> Spec a
    forall a. HasCallStack => [Char] -> a
    error [Char]
    "The input file provided is not a spec"

    -- | Process input file, it contains a valid diagram or specification, and
    -- return its abstract representation.
    parseInputFile :: FilePath
                   -> String
                   -> String
                   -> Maybe String
                   -> ExprPairT a
                   -> ExceptT ErrorTriplet IO (InputFile a)
    parseInputFile :: forall a.
    [Char]
    -> [Char]
    -> [Char]
    -> Maybe [Char]
    -> ExprPairT a
    -> ExceptT ErrorTriplet IO (InputFile a)
    parseInputFile [Char]
    fp [Char]
    formatName [Char]
    propFormatName Maybe [Char]
    propVia ExprPairT a
    exprT
        | [Char] -> Bool
    isDiagramFormat [Char]
    formatName
        = Diagram -> InputFile a
    forall a. Diagram -> InputFile a
    InputFileDiagram (Diagram -> InputFile a)
    -> ExceptT ErrorTriplet IO Diagram
    -> ExceptT ErrorTriplet IO (InputFile a)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$>
            [Char]
    -> DiagramFormat -> ExprPair -> ExceptT ErrorTriplet IO Diagram
    readDiagram [Char]
    fp DiagramFormat
    diagramFormat (ExprPairT a -> ExprPair
    forall a. ExprPairT a -> ExprPair
    ExprPair ExprPairT a
    exprT)

        | Bool
    otherwise
        = Spec a -> InputFile a
    forall a. Spec a -> InputFile a
    InputFileSpec (Spec a -> InputFile a)
    -> ExceptT ErrorTriplet IO (Spec a)
    -> ExceptT ErrorTriplet IO (InputFile a)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$>
            [Char]
    -> [Char]
    -> [Char]
    -> Maybe [Char]
    -> ExprPairT a
    -> ExceptT ErrorTriplet IO (Spec a)
    forall a.
    [Char]
    -> [Char]
    -> [Char]
    -> Maybe [Char]
    -> ExprPairT a
    -> ExceptT ErrorTriplet IO (Spec a)
    readInputFile [Char]
    fp [Char]
    formatName [Char]
    propFormatName Maybe [Char]
    propVia ExprPairT a
    exprT

      where

        isDiagramFormat :: String -> Bool
        isDiagramFormat :: [Char] -> Bool
    isDiagramFormat [Char]
    fName = [Char]
    fName [Char] -> [[Char]] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [ [Char]
    "dot", [Char]
    "mermaid" ]

        diagramFormat :: DiagramFormat
        diagramFormat :: DiagramFormat
    diagramFormat
          | [Char]
    formatName [Char] -> [Char] -> Bool
    forall a. Eq a => a -> a -> Bool
    == [Char]
    "dot"     = DiagramFormat
    Dot
          | [Char]
    formatName [Char] -> [Char] -> Bool
    forall a. Eq a => a -> a -> Bool
    == [Char]
    "mermaid" = DiagramFormat
    Mermaid
          | Bool
    otherwise               = [Char] -> DiagramFormat
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> DiagramFormat) -> [Char] -> DiagramFormat
    forall a b. (a -> b) -> a -> b
    $
             [Char]
    "diagramFormat: Not a diagram format " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char] -> [Char]
    forall a. Show a => a -> [Char]
    show [Char]
    formatName

    -- | Process a variable selection file, if available, and return the variable
    -- names.
    parseVariablesFile :: Maybe FilePath
                       -> ExceptT ErrorTriplet IO (Maybe [String])
    parseVariablesFile :: Maybe [Char] -> ExceptT ErrorTriplet IO (Maybe [[Char]])
    parseVariablesFile Maybe [Char]
    Nothing   = Maybe [[Char]] -> ExceptT ErrorTriplet IO (Maybe [[Char]])
    forall a. a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe [[Char]]
    forall a. Maybe a
    Nothing
    parseVariablesFile (Just [Char]
    fp) = do
      -- Fail if the file cannot be opened.
      varNamesE <- IO (Either SomeException [[Char]])
    -> ExceptT ErrorTriplet IO (Either SomeException [[Char]])
    forall a. IO a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Either SomeException [[Char]])
     -> ExceptT ErrorTriplet IO (Either SomeException [[Char]]))
    -> IO (Either SomeException [[Char]])
    -> ExceptT ErrorTriplet IO (Either SomeException [[Char]])
    forall a b. (a -> b) -> a -> b
    $ IO [[Char]] -> IO (Either SomeException [[Char]])
    forall e a. Exception e => IO a -> IO (Either e a)
    E.try (IO [[Char]] -> IO (Either SomeException [[Char]]))
    -> IO [[Char]] -> IO (Either SomeException [[Char]])
    forall a b. (a -> b) -> a -> b
    $ [Char] -> [[Char]]
    lines ([Char] -> [[Char]]) -> IO [Char] -> IO [[Char]]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> [Char] -> IO [Char]
    readFile [Char]
    fp
      case (varNamesE :: Either E.SomeException [String]) of
        Left SomeException
    _         -> ErrorTriplet -> ExceptT ErrorTriplet IO (Maybe [[Char]])
    forall a. ErrorTriplet -> ExceptT ErrorTriplet IO a
    forall e (m :: * -> *) a. MonadError e m => e -> m a
    throwError (ErrorTriplet -> ExceptT ErrorTriplet IO (Maybe [[Char]]))
    -> ErrorTriplet -> ExceptT ErrorTriplet IO (Maybe [[Char]])
    forall a b. (a -> b) -> a -> b
    $ [Char] -> ErrorTriplet
    cannotOpenVarFile [Char]
    fp
        Right [[Char]]
    varNames -> Maybe [[Char]] -> ExceptT ErrorTriplet IO (Maybe [[Char]])
    forall a. a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Maybe [[Char]] -> ExceptT ErrorTriplet IO (Maybe [[Char]]))
    -> Maybe [[Char]] -> ExceptT ErrorTriplet IO (Maybe [[Char]])
    forall a b. (a -> b) -> a -> b
    $ [[Char]] -> Maybe [[Char]]
    forall a. a -> Maybe a
    Just [[Char]]
    varNames

    -- | Process a requirements / handlers list file, if available, and return the
    -- handler names.
    parseRequirementsListFile :: Maybe FilePath
                              -> ExceptT ErrorTriplet IO (Maybe [String])
    parseRequirementsListFile :: Maybe [Char] -> ExceptT ErrorTriplet IO (Maybe [[Char]])
    parseRequirementsListFile Maybe [Char]
    Nothing   = Maybe [[Char]] -> ExceptT ErrorTriplet IO (Maybe [[Char]])
    forall a. a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe [[Char]]
    forall a. Maybe a
    Nothing
    parseRequirementsListFile (Just [Char]
    fp) =
      IO (Either ErrorTriplet (Maybe [[Char]]))
    -> ExceptT ErrorTriplet IO (Maybe [[Char]])
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT (IO (Either ErrorTriplet (Maybe [[Char]]))
     -> ExceptT ErrorTriplet IO (Maybe [[Char]]))
    -> IO (Either ErrorTriplet (Maybe [[Char]]))
    -> ExceptT ErrorTriplet IO (Maybe [[Char]])
    forall a b. (a -> b) -> a -> b
    $ ErrorTriplet
    -> Either SomeException (Maybe [[Char]])
    -> Either ErrorTriplet (Maybe [[Char]])
    forall c b. c -> Either SomeException b -> Either c b
    makeLeftE ([Char] -> ErrorTriplet
    cannotOpenHandlersFile [Char]
    fp) (Either SomeException (Maybe [[Char]])
     -> Either ErrorTriplet (Maybe [[Char]]))
    -> IO (Either SomeException (Maybe [[Char]]))
    -> IO (Either ErrorTriplet (Maybe [[Char]]))
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$>
        (IO (Maybe [[Char]]) -> IO (Either SomeException (Maybe [[Char]]))
    forall e a. Exception e => IO a -> IO (Either e a)
    E.try (IO (Maybe [[Char]]) -> IO (Either SomeException (Maybe [[Char]])))
    -> IO (Maybe [[Char]])
    -> IO (Either SomeException (Maybe [[Char]]))
    forall a b. (a -> b) -> a -> b
    $ [[Char]] -> Maybe [[Char]]
    forall a. a -> Maybe a
    Just ([[Char]] -> Maybe [[Char]])
    -> ([Char] -> [[Char]]) -> [Char] -> Maybe [[Char]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> [[Char]]
    lines ([Char] -> Maybe [[Char]]) -> IO [Char] -> IO (Maybe [[Char]])
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> [Char] -> IO [Char]
    readFile [Char]
    fp)

    -- | Read a list of variable DBs.
    openVarDBFiles :: VariableDB
                   -> [FilePath]
                   -> ExceptT ErrorTriplet IO VariableDB
    openVarDBFiles :: VariableDB -> [[Char]] -> ExceptT ErrorTriplet IO VariableDB
    openVarDBFiles VariableDB
    acc []     = VariableDB -> ExceptT ErrorTriplet IO VariableDB
    forall a. a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return VariableDB
    acc
    openVarDBFiles VariableDB
    acc ([Char]
    x:[[Char]]
    xs) = do
        file <- Maybe [Char] -> ExceptT ErrorTriplet IO VariableDB
    parseVarDBFile ([Char] -> Maybe [Char]
    forall a. a -> Maybe a
    Just [Char]
    x)
        acc' <- mergeVariableDB acc file
        openVarDBFiles acc' xs

      where

        -- Process a variable database file, if available.
        parseVarDBFile :: Maybe FilePath
                       -> ExceptT ErrorTriplet IO VariableDB
        parseVarDBFile :: Maybe [Char] -> ExceptT ErrorTriplet IO VariableDB
    parseVarDBFile Maybe [Char]
    Nothing   = VariableDB -> ExceptT ErrorTriplet IO VariableDB
    forall a. a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return VariableDB
    emptyVariableDB
        parseVarDBFile (Just [Char]
    fn) =
          IO (Either ErrorTriplet VariableDB)
    -> ExceptT ErrorTriplet IO VariableDB
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT (IO (Either ErrorTriplet VariableDB)
     -> ExceptT ErrorTriplet IO VariableDB)
    -> IO (Either ErrorTriplet VariableDB)
    -> ExceptT ErrorTriplet IO VariableDB
    forall a b. (a -> b) -> a -> b
    $ ErrorTriplet
    -> Either [Char] VariableDB -> Either ErrorTriplet VariableDB
    forall c a b. c -> Either a b -> Either c b
    makeLeft ([Char] -> ErrorTriplet
    cannotOpenDB [Char]
    fn) (Either [Char] VariableDB -> Either ErrorTriplet VariableDB)
    -> IO (Either [Char] VariableDB)
    -> IO (Either ErrorTriplet VariableDB)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$>
            [Char] -> IO (Either [Char] VariableDB)
    forall a. FromJSON a => [Char] -> IO (Either [Char] a)
    eitherDecodeFileStrict [Char]
    fn

    -- | Read a list of variable DBs, as well as the default variable DB.
    openVarDBFilesWithDefault :: [FilePath]
                              -> ExceptT ErrorTriplet IO VariableDB
    openVarDBFilesWithDefault :: [[Char]] -> ExceptT ErrorTriplet IO VariableDB
    openVarDBFilesWithDefault [[Char]]
    files = do
      dataDir <- IO [Char] -> ExceptT ErrorTriplet IO [Char]
    forall a. IO a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO [Char]
    getDataDir
      let defaultDB = [Char]
    dataDir [Char] -> [Char] -> [Char]
    </> [Char]
    "data" [Char] -> [Char] -> [Char]
    </> [Char]
    "variable-db.json"
      openVarDBFiles emptyVariableDB (files ++ [defaultDB])

    -- | Process a JSON file with additional template variables to make available
    -- during template expansion.
    parseTemplateVarsFile :: Maybe FilePath
                          -> ExceptT ErrorTriplet IO Value
    parseTemplateVarsFile :: Maybe [Char] -> ExceptT ErrorTriplet IO Value
    parseTemplateVarsFile Maybe [Char]
    Nothing   = Value -> ExceptT ErrorTriplet IO Value
    forall a. a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Value -> ExceptT ErrorTriplet IO Value)
    -> Value -> ExceptT ErrorTriplet IO Value
    forall a b. (a -> b) -> a -> b
    $ [Pair] -> Value
    object []
    parseTemplateVarsFile (Just [Char]
    fp) = do
      content <- IO (Either [Char] ByteString)
    -> ExceptT ErrorTriplet IO (Either [Char] ByteString)
    forall a. IO a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Either [Char] ByteString)
     -> ExceptT ErrorTriplet IO (Either [Char] ByteString))
    -> IO (Either [Char] ByteString)
    -> ExceptT ErrorTriplet IO (Either [Char] ByteString)
    forall a b. (a -> b) -> a -> b
    $ [Char] -> IO (Either [Char] ByteString)
    B.safeReadFile [Char]
    fp
      let value = ByteString -> Either [Char] Value
    forall a. FromJSON a => ByteString -> Either [Char] a
    eitherDecode (ByteString -> Either [Char] Value)
    -> Either [Char] ByteString -> Either [Char] Value
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Either [Char] ByteString
    content
      case value of
        Right x :: Value
    x@(Object Object
    _) -> Value -> ExceptT ErrorTriplet IO Value
    forall a. a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Value
    x
        Right x :: Value
    x@Value
    Null       -> Value -> ExceptT ErrorTriplet IO Value
    forall a. a -> ExceptT ErrorTriplet IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Value
    x
        Right Value
    _            -> ErrorTriplet -> ExceptT ErrorTriplet IO Value
    forall a. ErrorTriplet -> ExceptT ErrorTriplet IO a
    forall e (m :: * -> *) a. MonadError e m => e -> m a
    throwError ([Char] -> ErrorTriplet
    cannotReadObjectTemplateVars [Char]
    fp)
        Either [Char] Value
    _                  -> ErrorTriplet -> ExceptT ErrorTriplet IO Value
    forall a. ErrorTriplet -> ExceptT ErrorTriplet IO a
    forall e (m :: * -> *) a. MonadError e m => e -> m a
    throwError ([Char] -> ErrorTriplet
    cannotOpenTemplateVars [Char]
    fp)

    -- | Check that the arguments provided are sufficient to operate.
    --
    -- The backend provides several modes of operation, which are selected
    -- by providing different arguments to the `ros` command.
    --
    -- When an input specification file is provided, the variables and requirements
    -- defined in it are used unless variables or handlers files are provided, in
    -- which case the latter take priority.
    --
    -- If an input file is not provided, then the user must provide BOTH a variable
    -- list, and a list of handlers.
    checkArguments :: Maybe (InputFile a)
                   -> Maybe [String]
                   -> Maybe [String]
                   -> Either ErrorTriplet ()
    checkArguments :: forall a.
    Maybe (InputFile a)
    -> Maybe [[Char]] -> Maybe [[Char]] -> Either ErrorTriplet ()
    checkArguments Maybe (InputFile a)
    Nothing Maybe [[Char]]
    Nothing   Maybe [[Char]]
    Nothing   = ErrorTriplet -> Either ErrorTriplet ()
    forall a b. a -> Either a b
    Left ErrorTriplet
    wrongArguments
    checkArguments Maybe (InputFile a)
    Nothing Maybe [[Char]]
    Nothing   Maybe [[Char]]
    _         = ErrorTriplet -> Either ErrorTriplet ()
    forall a b. a -> Either a b
    Left ErrorTriplet
    wrongArguments
    checkArguments Maybe (InputFile a)
    Nothing Maybe [[Char]]
    _         Maybe [[Char]]
    Nothing   = ErrorTriplet -> Either ErrorTriplet ()
    forall a b. a -> Either a b
    Left ErrorTriplet
    wrongArguments
    checkArguments Maybe (InputFile a)
    _       (Just []) Maybe [[Char]]
    _         = ErrorTriplet -> Either ErrorTriplet ()
    forall a b. a -> Either a b
    Left ErrorTriplet
    wrongArguments
    checkArguments Maybe (InputFile a)
    _       Maybe [[Char]]
    _         (Just []) = ErrorTriplet -> Either ErrorTriplet ()
    forall a b. a -> Either a b
    Left ErrorTriplet
    wrongArguments
    checkArguments Maybe (InputFile a)
    _       Maybe [[Char]]
    _         Maybe [[Char]]
    _         = () -> Either ErrorTriplet ()
    forall a b. b -> Either a b
    Right ()

    -- | Extract the variables from a specification, and sanitize them.
    specExtractExternalVariables :: Maybe (Spec a) -> [String]
    specExtractExternalVariables :: forall a. Maybe (Spec a) -> [[Char]]
    specExtractExternalVariables Maybe (Spec a)
    Nothing   = []
    specExtractExternalVariables (Just Spec a
    cs) = ([Char] -> [Char]) -> [[Char]] -> [[Char]]
    forall a b. (a -> b) -> [a] -> [b]
    map [Char] -> [Char]
    sanitizeLCIdentifier
                                           ([[Char]] -> [[Char]]) -> [[Char]] -> [[Char]]
    forall a b. (a -> b) -> a -> b
    $ (ExternalVariableDef -> [Char])
    -> [ExternalVariableDef] -> [[Char]]
    forall a b. (a -> b) -> [a] -> [b]
    map ExternalVariableDef -> [Char]
    externalVariableName
                                           ([ExternalVariableDef] -> [[Char]])
    -> [ExternalVariableDef] -> [[Char]]
    forall a b. (a -> b) -> a -> b
    $ Spec a -> [ExternalVariableDef]
    forall a. Spec a -> [ExternalVariableDef]
    externalVariables Spec a
    cs

    -- | Extract the requirements from a specification, and sanitize them to match
    -- the names of the handlers used by Copilot.
    specExtractHandlers :: Maybe (Spec a) -> [(String, Maybe String)]
    specExtractHandlers :: forall a. Maybe (Spec a) -> [([Char], Maybe [Char])]
    specExtractHandlers Maybe (Spec a)
    Nothing   = []
    specExtractHandlers (Just Spec a
    cs) = (Requirement a -> ([Char], Maybe [Char]))
    -> [Requirement a] -> [([Char], Maybe [Char])]
    forall a b. (a -> b) -> [a] -> [b]
    map Requirement a -> ([Char], Maybe [Char])
    forall {a}. Requirement a -> ([Char], Maybe [Char])
    extractReqData
                                  ([Requirement a] -> [([Char], Maybe [Char])])
    -> [Requirement a] -> [([Char], Maybe [Char])]
    forall a b. (a -> b) -> a -> b
    $ Spec a -> [Requirement a]
    forall a. Spec a -> [Requirement a]
    requirements Spec a
    cs
      where
        extractReqData :: Requirement a -> ([Char], Maybe [Char])
    extractReqData Requirement a
    r =
          ([Char] -> [Char]
    handlerNameF (Requirement a -> [Char]
    forall a. Requirement a -> [Char]
    requirementName Requirement a
    r), Requirement a -> Maybe [Char]
    forall a. Requirement a -> Maybe [Char]
    requirementResultType Requirement a
    r)

        handlerNameF :: [Char] -> [Char]
    handlerNameF = ([Char]
    "handler" [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++) ([Char] -> [Char]) -> ([Char] -> [Char]) -> [Char] -> [Char]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> [Char]
    sanitizeUCIdentifier

    -- * Errors

    -- | Process a computation that can fail with an error code, and turn it into a
    -- computation that returns a 'Result'.
    processResult :: Monad m => ExceptT ErrorTriplet m a -> m (Result ErrorCode)
    processResult :: forall (m :: * -> *) a.
    Monad m =>
    ExceptT ErrorTriplet m a -> m (Result ErrorCode)
    processResult ExceptT ErrorTriplet m a
    m = do
      r <- ExceptT ErrorTriplet m a -> m (Either ErrorTriplet a)
    forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
    runExceptT ExceptT ErrorTriplet m a
    m
      case r of
        Left (ErrorTriplet ErrorCode
    errorCode [Char]
    msg Location
    location)
          -> Result ErrorCode -> m (Result ErrorCode)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Result ErrorCode -> m (Result ErrorCode))
    -> Result ErrorCode -> m (Result ErrorCode)
    forall a b. (a -> b) -> a -> b
    $ ErrorCode -> [Char] -> Location -> Result ErrorCode
    forall a. a -> [Char] -> Location -> Result a
    Error ErrorCode
    errorCode [Char]
    msg Location
    location
        Either ErrorTriplet a
    _ -> Result ErrorCode -> m (Result ErrorCode)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Result ErrorCode
    forall a. Result a
    Success

    -- ** Error messages

    -- | Exception handler to deal with the case in which the arguments
    -- provided are incorrect.
    wrongArguments :: ErrorTriplet
    wrongArguments :: ErrorTriplet
    wrongArguments =
        ErrorCode -> [Char] -> Location -> ErrorTriplet
    ErrorTriplet ErrorCode
    ecWrongArguments [Char]
    msg Location
    LocationNothing
      where
        msg :: [Char]
    msg =
          [Char]
    "the arguments provided are insufficient: you must provide an input "
          [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    "specification, or both a variables and a handlers file."

    -- | Exception handler to deal with the case in which the variable DB cannot be
    -- opened.
    cannotOpenDB :: FilePath -> ErrorTriplet
    cannotOpenDB :: [Char] -> ErrorTriplet
    cannotOpenDB [Char]
    file =
        ErrorCode -> [Char] -> Location -> ErrorTriplet
    ErrorTriplet ErrorCode
    ecCannotOpenDBFile [Char]
    msg ([Char] -> Location
    LocationFile [Char]
    file)
      where
        msg :: [Char]
    msg =
          [Char]
    "cannot open variable DB file " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    file

    -- | Exception handler to deal with the case in which the variable file
    -- provided by the user cannot be opened.
    cannotOpenVarFile :: FilePath -> ErrorTriplet
    cannotOpenVarFile :: [Char] -> ErrorTriplet
    cannotOpenVarFile [Char]
    file =
        ErrorCode -> [Char] -> Location -> ErrorTriplet
    ErrorTriplet ErrorCode
    ecCannotOpenVarFile  [Char]
    msg ([Char] -> Location
    LocationFile [Char]
    file)
      where
        msg :: [Char]
    msg =
          [Char]
    "cannot open variable list file " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    file

    -- | Exception handler to deal with the case in which the handlers file cannot
    -- be opened.
    cannotOpenHandlersFile :: FilePath -> ErrorTriplet
    cannotOpenHandlersFile :: [Char] -> ErrorTriplet
    cannotOpenHandlersFile [Char]
    file =
        ErrorCode -> [Char] -> Location -> ErrorTriplet
    ErrorTriplet ErrorCode
    ecCannotOpenHandlersFile [Char]
    msg ([Char] -> Location
    LocationFile [Char]
    file)
      where
        msg :: [Char]
    msg =
          [Char]
    "cannot open handlers file " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    file

    -- | Exception handler to deal with the case in which the template vars file
    -- cannot be opened.
    cannotOpenTemplateVars :: FilePath -> ErrorTriplet
    cannotOpenTemplateVars :: [Char] -> ErrorTriplet
    cannotOpenTemplateVars [Char]
    file =
        ErrorCode -> [Char] -> Location -> ErrorTriplet
    ErrorTriplet ErrorCode
    ecCannotOpenTemplateVarsFile [Char]
    msg ([Char] -> Location
    LocationFile [Char]
    file)
      where
        msg :: [Char]
    msg =
          [Char]
    "Cannot open file with additional template variables: " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    file

    -- | Exception handler to deal with the case in which the template vars file
    -- cannot be opened.
    cannotReadObjectTemplateVars :: FilePath -> ErrorTriplet
    cannotReadObjectTemplateVars :: [Char] -> ErrorTriplet
    cannotReadObjectTemplateVars [Char]
    file =
        ErrorCode -> [Char] -> Location -> ErrorTriplet
    ErrorTriplet ErrorCode
    ecCannotReadObjectTemplateVarsFile [Char]
    msg ([Char] -> Location
    LocationFile [Char]
    file)
      where
        msg :: [Char]
    msg =
          [Char]
    "Cannot open file with additional template variables: " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    file

    -- | Exception handler to deal with the case of files that cannot be
    -- copied/generated due lack of space or permissions or some I/O error.
    cannotCopyTemplate :: ErrorTriplet
    cannotCopyTemplate :: ErrorTriplet
    cannotCopyTemplate =
        ErrorCode -> [Char] -> Location -> ErrorTriplet
    ErrorTriplet ErrorCode
    ecCannotCopyTemplate [Char]
    msg Location
    LocationNothing
      where
        msg :: [Char]
    msg =
          [Char]
    "Generation failed during copy/write operation. Check that"
          [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    " there's free space in the disk and that you have the necessary"
          [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    " permissions to write in the destination directory."

    -- ** Error codes

    -- | Error: wrong arguments provided.
    ecWrongArguments :: ErrorCode
    ecWrongArguments :: ErrorCode
    ecWrongArguments = ErrorCode
    1

    -- | Error: the variable DB provided by the user cannot be opened.
    ecCannotOpenDBFile :: ErrorCode
    ecCannotOpenDBFile :: ErrorCode
    ecCannotOpenDBFile = ErrorCode
    1

    -- | Error: the variable file provided by the user cannot be opened.
    ecCannotOpenVarFile :: ErrorCode
    ecCannotOpenVarFile :: ErrorCode
    ecCannotOpenVarFile = ErrorCode
    1

    -- | Error: the handlers file provided by the user cannot be opened.
    ecCannotOpenHandlersFile :: ErrorCode
    ecCannotOpenHandlersFile :: ErrorCode
    ecCannotOpenHandlersFile = ErrorCode
    1

    -- | Error: the template vars file provided by the user cannot be opened.
    ecCannotOpenTemplateVarsFile :: ErrorCode
    ecCannotOpenTemplateVarsFile :: ErrorCode
    ecCannotOpenTemplateVarsFile = ErrorCode
    1

    -- | Error: the template variables file passed does not contain a JSON object.
    ecCannotReadObjectTemplateVarsFile :: ErrorCode
    ecCannotReadObjectTemplateVarsFile :: ErrorCode
    ecCannotReadObjectTemplateVarsFile = ErrorCode
    1

    -- | Error: the files cannot be copied/generated due lack of space or
    -- permissions or some I/O error.
    ecCannotCopyTemplate :: ErrorCode
    ecCannotCopyTemplate :: ErrorCode
    ecCannotCopyTemplate = ErrorCode
    1

    -- * Auxiliary Functions

    -- | Return the path to the template directory.
    locateTemplateDir :: Maybe FilePath
                      -> FilePath
                      -> ExceptT e IO FilePath
    locateTemplateDir :: forall e. Maybe [Char] -> [Char] -> ExceptT e IO [Char]
    locateTemplateDir Maybe [Char]
    mTemplateDir [Char]
    name =
      case Maybe [Char]
    mTemplateDir of
        Just [Char]
    x  -> [Char] -> ExceptT e IO [Char]
    forall a. a -> ExceptT e IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [Char]
    x
        Maybe [Char]
    Nothing -> IO [Char] -> ExceptT e IO [Char]
    forall a. IO a -> ExceptT e IO a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO [Char] -> ExceptT e IO [Char])
    -> IO [Char] -> ExceptT e IO [Char]
    forall a b. (a -> b) -> a -> b
    $ do
          dataDir <- IO [Char]
    getDataDir
          return $ dataDir </> "templates" </> name

    -- | Replace the left Exception in an Either.
    makeLeftE :: c -> Either E.SomeException b -> Either c b
    makeLeftE :: forall c b. c -> Either SomeException b -> Either c b
    makeLeftE = c -> Either SomeException b -> Either c b
    forall c a b. c -> Either a b -> Either c b
    makeLeft