{-# LANGUAGE DeriveGeneric             #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# 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.
--
-- | Create <https://www.ros.org/ Robot Operating System> (ROS) applications
-- that subscribe to obtain data and call Copilot when new values arrive.
--
-- It is the user's responsibility to modify the generated Copilot/C/C++ code
-- to deal with the monitors they'd like to implement, and the data they must
-- manipulate.

{- HLINT ignore "Functor law" -}
module Command.ROSApp
    ( command
    , CommandOptions(..)
    , Node(Node)
    , ErrorCode
    )
  where

-- External imports
import           Control.Applicative  ((<|>))
import qualified Control.Exception    as E
import           Control.Monad.Except (ExceptT (..), liftEither)
import           Data.Aeson           (ToJSON (..))
import           Data.Maybe           (fromMaybe, mapMaybe, maybeToList)
import           GHC.Generics         (Generic)

-- External imports: auxiliary
import System.Directory.Extra (copyTemplate)

import qualified Command.Standalone

-- Internal imports: auxiliary
import Command.Result    ( Result (..) )
import Data.Either.Extra ( mapLeft )

-- Internal imports
import Command.Common                 (InputFile (..), cannotCopyTemplateF,
                                       checkArguments, combineInputFiles,
                                       locateTemplateDir,
                                       openVarDBFilesWithDefault,
                                       parseInputFile,
                                       parseRequirementsListFile,
                                       parseTemplateVarsFile,
                                       parseVariablesFile, processResult,
                                       specExtractExternalVariables,
                                       specExtractHandlers)
import Command.Errors                 (ErrorCode, ErrorTriplet (..))
import Command.VariableDB             (Connection (..), InputDef (..),
                                       TopicDef (..), TypeDef (..), VariableDB,
                                       findConnection, findInput, findTopic,
                                       findType, findTypeByType)
import Data.Aeson.Extra               (mergeObjects)
import Data.ExprPair                  (ExprPair (..), exprPair)
import Data.Location                  (Location (..))
import Data.Spec.Parser               (readInputExpr)
import Language.Trans.Diagram2Copilot (DiagramMode (..))

-- | Generate a new ROS application connected to Copilot.
command :: CommandOptions -- ^ Options to the ROS backend.
        -> IO (Result ErrorCode)
command :: CommandOptions -> IO (Result ErrorCode)
command CommandOptions
options = ExceptT ErrorTriplet IO () -> IO (Result ErrorCode)
forall (m :: * -> *) a.
Monad m =>
ExceptT ErrorTriplet m a -> m (Result ErrorCode)
processResult (ExceptT ErrorTriplet IO () -> IO (Result ErrorCode))
-> ExceptT ErrorTriplet IO () -> IO (Result ErrorCode)
forall a b. (a -> b) -> a -> b
$ do
    -- Obtain template dir
    templateDir <- Maybe FilePath -> FilePath -> ExceptT ErrorTriplet IO FilePath
forall e. Maybe FilePath -> FilePath -> ExceptT e IO FilePath
locateTemplateDir Maybe FilePath
mTemplateDir FilePath
"ros"

    templateVars <- parseTemplateVarsFile templateVarsF

    appData <- command' options functions

    let subst = Value -> Value -> Value
mergeObjects (AppData -> Value
forall a. ToJSON a => a -> Value
toJSON AppData
appData) Value
templateVars

    -- Expand template
    ExceptT $ fmap (mapLeft cannotCopyTemplateF) $ E.try $
      copyTemplate templateDir subst targetDir

  where

    targetDir :: FilePath
targetDir     = CommandOptions -> FilePath
commandTargetDir CommandOptions
options
    mTemplateDir :: Maybe FilePath
mTemplateDir  = CommandOptions -> Maybe FilePath
commandTemplateDir CommandOptions
options
    functions :: ExprPair
functions     = FilePath -> ExprPair
exprPair (CommandOptions -> FilePath
commandPropFormat CommandOptions
options)
    templateVarsF :: Maybe FilePath
templateVarsF = CommandOptions -> Maybe FilePath
commandExtraVars CommandOptions
options

-- | Generate application data describing core elements of a new ROS package
-- that implements the input requirements or diagrams.
command' :: CommandOptions
         -> ExprPair
         -> ExceptT ErrorTriplet IO AppData
command' :: CommandOptions -> ExprPair -> ExceptT ErrorTriplet IO AppData
command' CommandOptions
options (ExprPair ExprPairT a
exprT) = do
    -- Open files needed to fill in details in the template.
    vs    <- Maybe FilePath -> ExceptT ErrorTriplet IO (Maybe [FilePath])
parseVariablesFile Maybe FilePath
varNameFile
    rs    <- parseRequirementsListFile handlersFile
    varDB <- openVarDBFilesWithDefault varDBFile

    specT <- maybe
               (return Nothing)
               (fmap (Just . InputFileSpec) . readInputExpr')
               cExpr

    specF <- if null fpA
               then return Nothing
               else do
                 fpA' <- mapM readInputFile' fpA
                 let fpA'' = [InputFile a] -> [InputFile a]
forall a. [InputFile a] -> [InputFile a]
combineInputFiles [InputFile a]
fpA'
                 if length fpA'' > 1
                   then liftEither $ Left commandMultipleInputTypes
                   else pure $ Just $ head fpA''

    let spec = Maybe (InputFile a)
specT Maybe (InputFile a) -> Maybe (InputFile a) -> Maybe (InputFile a)
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe (InputFile a)
specF

    liftEither $ checkArguments spec vs rs

    copilotM <- traverse (\InputFile a
spec' -> InputFile a
-> Maybe FilePath -> [FilePath] -> ExceptT ErrorTriplet IO AppData
processSpec InputFile a
spec' Maybe FilePath
cExpr [FilePath]
fpA) spec

    let varNames = [FilePath] -> Maybe [FilePath] -> [FilePath]
forall a. a -> Maybe a -> a
fromMaybe (Maybe (InputFile a) -> [FilePath]
forall {a}. Maybe (InputFile a) -> [FilePath]
defaultVarNames Maybe (InputFile a)
spec) Maybe [FilePath]
vs
        monitors = [(FilePath, Maybe FilePath)]
-> ([FilePath] -> [(FilePath, Maybe FilePath)])
-> Maybe [FilePath]
-> [(FilePath, Maybe FilePath)]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Maybe (InputFile a) -> [(FilePath, Maybe FilePath)]
forall {a}. Maybe (InputFile a) -> [(FilePath, Maybe FilePath)]
defaultMonitors Maybe (InputFile a)
spec) ((FilePath -> (FilePath, Maybe FilePath))
-> [FilePath] -> [(FilePath, Maybe FilePath)]
forall a b. (a -> b) -> [a] -> [b]
map (\FilePath
x -> (FilePath
x, Maybe FilePath
forall a. Maybe a
Nothing))) Maybe [FilePath]
rs

    let appData =
          [VarDecl]
-> [Monitor] -> Maybe AppData -> [Node] -> [VarDecl] -> AppData
AppData [VarDecl]
variables [Monitor]
monitors' Maybe AppData
copilotM [Node]
testingAdditionalApps [VarDecl]
testingVars

        variables = (FilePath -> Maybe VarDecl) -> [FilePath] -> [VarDecl]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (VariableDB -> FilePath -> Maybe VarDecl
variableMap VariableDB
varDB) [FilePath]
varNames
        monitors' = ((FilePath, Maybe FilePath) -> Maybe Monitor)
-> [(FilePath, Maybe FilePath)] -> [Monitor]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (VariableDB -> (FilePath, Maybe FilePath) -> Maybe Monitor
monitorMap VariableDB
varDB) [(FilePath, Maybe FilePath)]
monitors

        testingVars
          | [FilePath] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
testingLimitedVars
          = [VarDecl]
variables
          | Bool
otherwise
          = (VarDecl -> Bool) -> [VarDecl] -> [VarDecl]
forall a. (a -> Bool) -> [a] -> [a]
filter (\VarDecl
x -> VarDecl -> FilePath
varDeclName VarDecl
x FilePath -> [FilePath] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [FilePath]
testingLimitedVars) [VarDecl]
variables

    return appData

  where

    cExpr :: Maybe FilePath
cExpr          = CommandOptions -> Maybe FilePath
commandConditionExpr CommandOptions
options
    fpA :: [FilePath]
fpA            = CommandOptions -> [FilePath]
commandInputFiles CommandOptions
options
    varNameFile :: Maybe FilePath
varNameFile    = CommandOptions -> Maybe FilePath
commandVariables CommandOptions
options
    varDBFile :: [FilePath]
varDBFile      = Maybe FilePath -> [FilePath]
forall a. Maybe a -> [a]
maybeToList (Maybe FilePath -> [FilePath]) -> Maybe FilePath -> [FilePath]
forall a b. (a -> b) -> a -> b
$ CommandOptions -> Maybe FilePath
commandVariableDB CommandOptions
options
    handlersFile :: Maybe FilePath
handlersFile   = CommandOptions -> Maybe FilePath
commandHandlers CommandOptions
options
    formatName :: FilePath
formatName     = CommandOptions -> FilePath
commandFormat CommandOptions
options
    propFormatName :: FilePath
propFormatName = CommandOptions -> FilePath
commandPropFormat CommandOptions
options
    propVia :: Maybe FilePath
propVia        = CommandOptions -> Maybe FilePath
commandPropVia CommandOptions
options

    readInputExpr' :: FilePath -> ExceptT ErrorTriplet IO (Spec a)
readInputExpr' FilePath
e =
      FilePath
-> FilePath
-> Maybe FilePath
-> ExprPairT a
-> ExceptT ErrorTriplet IO (Spec a)
forall a.
FilePath
-> FilePath
-> Maybe FilePath
-> ExprPairT a
-> ExceptT ErrorTriplet IO (Spec a)
readInputExpr FilePath
e FilePath
propFormatName Maybe FilePath
propVia ExprPairT a
exprT

    readInputFile' :: FilePath -> ExceptT ErrorTriplet IO (InputFile a)
readInputFile' FilePath
f =
      FilePath
-> FilePath
-> FilePath
-> Maybe FilePath
-> ExprPairT a
-> ExceptT ErrorTriplet IO (InputFile a)
forall a.
FilePath
-> FilePath
-> FilePath
-> Maybe FilePath
-> ExprPairT a
-> ExceptT ErrorTriplet IO (InputFile a)
parseInputFile FilePath
f FilePath
formatName FilePath
propFormatName Maybe FilePath
propVia ExprPairT a
exprT

    processSpec :: InputFile a
-> Maybe FilePath -> [FilePath] -> ExceptT ErrorTriplet IO AppData
processSpec InputFile a
spec' Maybe FilePath
expr' [FilePath]
fp' =
      Maybe FilePath
-> [FilePath]
-> FilePath
-> [(FilePath, FilePath)]
-> ExprPairT a
-> InputFile a
-> DiagramMode
-> ExceptT ErrorTriplet IO AppData
forall a.
Maybe FilePath
-> [FilePath]
-> FilePath
-> [(FilePath, FilePath)]
-> ExprPairT a
-> InputFile a
-> DiagramMode
-> ExceptT ErrorTriplet IO AppData
Command.Standalone.commandLogic
        Maybe FilePath
expr'
        [FilePath]
fp'
        FilePath
"copilot"
        []
        ExprPairT a
exprT
        InputFile a
spec'
        DiagramMode
ComputeState

    defaultVarNames :: Maybe (InputFile a) -> [FilePath]
defaultVarNames Maybe (InputFile a)
spec = case Maybe (InputFile a)
spec of
      Just (InputFileSpec Spec a
spec') -> Maybe (Spec a) -> [FilePath]
forall a. Maybe (Spec a) -> [FilePath]
specExtractExternalVariables (Spec a -> Maybe (Spec a)
forall a. a -> Maybe a
Just Spec a
spec')
      Just (InputFileDiagram Diagram
_)  -> []
      Maybe (InputFile a)
Nothing                    -> Maybe (Spec (ZonkAny 1)) -> [FilePath]
forall a. Maybe (Spec a) -> [FilePath]
specExtractExternalVariables Maybe (Spec (ZonkAny 1))
forall a. Maybe a
Nothing

    defaultMonitors :: Maybe (InputFile a) -> [(FilePath, Maybe FilePath)]
defaultMonitors Maybe (InputFile a)
spec = case Maybe (InputFile a)
spec of
      Just (InputFileSpec Spec a
spec') -> Maybe (Spec a) -> [(FilePath, Maybe FilePath)]
forall a. Maybe (Spec a) -> [(FilePath, Maybe FilePath)]
specExtractHandlers (Spec a -> Maybe (Spec a)
forall a. a -> Maybe a
Just Spec a
spec')
      Just (InputFileDiagram Diagram
_)  -> [ (FilePath
"handler", FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
"uint8_t" ) ]
      Maybe (InputFile a)
Nothing                    -> Maybe (Spec (ZonkAny 0)) -> [(FilePath, Maybe FilePath)]
forall a. Maybe (Spec a) -> [(FilePath, Maybe FilePath)]
specExtractHandlers Maybe (Spec (ZonkAny 0))
forall a. Maybe a
Nothing

    testingAdditionalApps :: [Node]
testingAdditionalApps = CommandOptions -> [Node]
commandTestingApps CommandOptions
options
    testingLimitedVars :: [FilePath]
testingLimitedVars    = CommandOptions -> [FilePath]
commandTestingVars CommandOptions
options

-- ** Argument processing

-- | Options used to customize the conversion of specifications to ROS
-- applications.
data CommandOptions = CommandOptions
  { CommandOptions -> Maybe FilePath
commandConditionExpr :: Maybe String   -- ^ Trigger condition.
  , CommandOptions -> [FilePath]
commandInputFiles    :: [FilePath]     -- ^ Input specification files.
  , CommandOptions -> FilePath
commandTargetDir     :: FilePath       -- ^ Target directory where the
                                           -- application should be created.
  , CommandOptions -> Maybe FilePath
commandTemplateDir   :: Maybe FilePath -- ^ Directory where the template is
                                           -- to be found.
  , CommandOptions -> Maybe FilePath
commandVariables     :: Maybe FilePath -- ^ File containing a list of
                                           -- variables to make available to
                                           -- Copilot.
  , CommandOptions -> Maybe FilePath
commandVariableDB    :: Maybe FilePath -- ^ File containing a list of known
                                           -- variables with their types and
                                           -- the message IDs they can be
                                           -- obtained from.
  , CommandOptions -> Maybe FilePath
commandHandlers      :: Maybe FilePath -- ^ File containing a list of
                                           -- handlers used in the Copilot
                                           -- specification. The handlers are
                                           -- assumed to receive no arguments.
  , CommandOptions -> FilePath
commandFormat        :: String         -- ^ Format of the input file.
  , CommandOptions -> FilePath
commandPropFormat    :: String         -- ^ Format used for input
                                           -- properties.
  , CommandOptions -> Maybe FilePath
commandPropVia       :: Maybe String   -- ^ Use external command to
                                           -- pre-process system properties.
  , CommandOptions -> Maybe FilePath
commandExtraVars     :: Maybe FilePath -- ^ File containing additional
                                           -- variables to make available to
                                           -- the template.
  , CommandOptions -> [Node]
commandTestingApps   :: [Node]         -- ^ Additional applications to turn
                                           -- on during testing.
  , CommandOptions -> [FilePath]
commandTestingVars   :: [String]       -- ^ Limited list of variables to
                                           -- use for testing.
  }

-- | Return the variable information needed to generate declarations
-- and subscriptions for a given variable name and variable database.
variableMap :: VariableDB
            -> String
            -> Maybe VarDecl
variableMap :: VariableDB -> FilePath -> Maybe VarDecl
variableMap VariableDB
varDB FilePath
varName = do
  inputDef <- VariableDB -> FilePath -> Maybe InputDef
findInput VariableDB
varDB FilePath
varName
  mid      <- connectionTopic <$> findConnection inputDef "ros/message"
  topicDef <- findTopic varDB "ros/message" mid
  typeVar' <- maybe
                (inputType inputDef)
                (Just . typeToType)
                (findType varDB varName "ros/variable" "C")
  let typeMsg' = FilePath -> (TypeDef -> FilePath) -> Maybe TypeDef -> FilePath
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
                   (TopicDef -> FilePath
topicType TopicDef
topicDef)
                   TypeDef -> FilePath
typeFromType
                   (VariableDB -> FilePath -> FilePath -> FilePath -> Maybe TypeDef
findType VariableDB
varDB FilePath
varName FilePath
"ros/message" FilePath
"C")

      fieldMsg = TypeDef -> Maybe FilePath
typeFromField (TypeDef -> Maybe FilePath) -> Maybe TypeDef -> Maybe FilePath
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< VariableDB -> FilePath -> FilePath -> FilePath -> Maybe TypeDef
findType VariableDB
varDB FilePath
varName FilePath
"ros/message" FilePath
"C"

  return $
    VarDecl varName typeVar' mid typeMsg' fieldMsg (randomBaseType typeVar')

-- | Return the monitor information needed to generate declarations and
-- publishers for the given monitor info, and variable database.
monitorMap :: VariableDB
           -> (String, Maybe String)
           -> Maybe Monitor
monitorMap :: VariableDB -> (FilePath, Maybe FilePath) -> Maybe Monitor
monitorMap VariableDB
varDB (FilePath
monitorName, Maybe FilePath
Nothing) =
  Monitor -> Maybe Monitor
forall a. a -> Maybe a
Just (Monitor -> Maybe Monitor) -> Monitor -> Maybe Monitor
forall a b. (a -> b) -> a -> b
$ FilePath -> Maybe FilePath -> Maybe FilePath -> Monitor
Monitor FilePath
monitorName Maybe FilePath
forall a. Maybe a
Nothing Maybe FilePath
forall a. Maybe a
Nothing
monitorMap VariableDB
varDB (FilePath
monitorName, Just FilePath
ty) = do
  let ty1 :: FilePath
ty1 = FilePath -> (TypeDef -> FilePath) -> Maybe TypeDef -> FilePath
forall b a. b -> (a -> b) -> Maybe a -> b
maybe FilePath
ty TypeDef -> FilePath
typeFromType (Maybe TypeDef -> FilePath) -> Maybe TypeDef -> FilePath
forall a b. (a -> b) -> a -> b
$ VariableDB -> FilePath -> FilePath -> FilePath -> Maybe TypeDef
findTypeByType VariableDB
varDB FilePath
"ros/variable" FilePath
"C" FilePath
ty
  ty2 <- TypeDef -> FilePath
typeFromType (TypeDef -> FilePath) -> Maybe TypeDef -> Maybe FilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> VariableDB -> FilePath -> FilePath -> FilePath -> Maybe TypeDef
findTypeByType VariableDB
varDB FilePath
"ros/message" FilePath
"C" FilePath
ty
  return $ Monitor monitorName (Just ty1) (Just ty2)

-- | The declaration of a variable in C, with a given type and name.
data VarDecl = VarDecl
    { VarDecl -> FilePath
varDeclName     :: String
    , VarDecl -> FilePath
varDeclType     :: String
    , VarDecl -> FilePath
varDeclId       :: String
    , VarDecl -> FilePath
varDeclMsgType  :: String
    , VarDecl -> Maybe FilePath
varDeclMsgField :: Maybe String
    , VarDecl -> FilePath
varDeclRandom   :: String
    }
  deriving (forall x. VarDecl -> Rep VarDecl x)
-> (forall x. Rep VarDecl x -> VarDecl) -> Generic VarDecl
forall x. Rep VarDecl x -> VarDecl
forall x. VarDecl -> Rep VarDecl x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. VarDecl -> Rep VarDecl x
from :: forall x. VarDecl -> Rep VarDecl x
$cto :: forall x. Rep VarDecl x -> VarDecl
to :: forall x. Rep VarDecl x -> VarDecl
Generic

instance ToJSON VarDecl

-- | The name of a handler associated to each condition, and the type
-- of value it receives, together with the type for the message.
data Monitor = Monitor
    { Monitor -> FilePath
monitorName    :: String
    , Monitor -> Maybe FilePath
monitorType    :: Maybe String
    , Monitor -> Maybe FilePath
monitorMsgType :: Maybe String
    }
  deriving (forall x. Monitor -> Rep Monitor x)
-> (forall x. Rep Monitor x -> Monitor) -> Generic Monitor
forall x. Rep Monitor x -> Monitor
forall x. Monitor -> Rep Monitor x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Monitor -> Rep Monitor x
from :: forall x. Monitor -> Rep Monitor x
$cto :: forall x. Rep Monitor x -> Monitor
to :: forall x. Rep Monitor x -> Monitor
Generic

instance ToJSON Monitor

-- | A package-qualified ROS 2 node name.
data Node = Node
    { Node -> FilePath
nodePackage :: String
    , Node -> FilePath
nodeName    :: String
    }
  deriving (forall x. Node -> Rep Node x)
-> (forall x. Rep Node x -> Node) -> Generic Node
forall x. Rep Node x -> Node
forall x. Node -> Rep Node x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Node -> Rep Node x
from :: forall x. Node -> Rep Node x
$cto :: forall x. Rep Node x -> Node
to :: forall x. Rep Node x -> Node
Generic

instance ToJSON Node

-- | Data that may be relevant to generate a ROS application.
data AppData = AppData
    { AppData -> [VarDecl]
variables        :: [VarDecl]
    , AppData -> [Monitor]
monitors         :: [Monitor]
    , AppData -> Maybe AppData
copilot          :: Maybe Command.Standalone.AppData
    , AppData -> [Node]
testingApps      :: [Node]
    , AppData -> [VarDecl]
testingVariables :: [VarDecl]
    }
  deriving ((forall x. AppData -> Rep AppData x)
-> (forall x. Rep AppData x -> AppData) -> Generic AppData
forall x. Rep AppData x -> AppData
forall x. AppData -> Rep AppData x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. AppData -> Rep AppData x
from :: forall x. AppData -> Rep AppData x
$cto :: forall x. Rep AppData x -> AppData
to :: forall x. Rep AppData x -> AppData
Generic)

instance ToJSON AppData

-- | Name of the function to be used to generate random values of a given type.
randomBaseType :: String -- ^ Type to generate random values of.
               -> String
randomBaseType :: FilePath -> FilePath
randomBaseType FilePath
ty = case FilePath
ty of
  FilePath
"bool"     -> FilePath
"randomBool"
  FilePath
"uint8_t"  -> FilePath
"randomInt"
  FilePath
"uint16_t" -> FilePath
"randomInt"
  FilePath
"uint32_t" -> FilePath
"randomInt"
  FilePath
"uint64_t" -> FilePath
"randomInt"
  FilePath
"int8_t"   -> FilePath
"randomInt"
  FilePath
"int16_t"  -> FilePath
"randomInt"
  FilePath
"int32_t"  -> FilePath
"randomInt"
  FilePath
"int64_t"  -> FilePath
"randomInt"
  FilePath
"float"    -> FilePath
"randomFloat"
  FilePath
"double"   -> FilePath
"randomFloat"
  FilePath
def        -> FilePath
def

-- | Error message associated to having multiple input files of incompatible
-- types.
commandMultipleInputTypes :: ErrorTriplet
commandMultipleInputTypes :: ErrorTriplet
commandMultipleInputTypes =
    ErrorCode -> FilePath -> Location -> ErrorTriplet
ErrorTriplet ErrorCode
ecMultipleInputTypes FilePath
msg Location
LocationNothing
  where
    msg :: FilePath
msg =
      FilePath
"Too many inputs provided. Provide one diagram or multiple specs."

-- | Error: multiple inputs of incompatible types.
ecMultipleInputTypes :: ErrorCode
ecMultipleInputTypes :: ErrorCode
ecMultipleInputTypes = ErrorCode
1