    -- Copyright 2020 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.
    --
    -- | Generate C methods that process NASA Core Flight System messages dealing
    -- with the structs defined in a header file.
    --
    -- This module makes use of "Language.Trans.CStructs2MsgHandlers", which does
    -- most of the work.
    module Command.CStructs2MsgHandlers
        ( cstructs2MsgHandlers
        , ErrorCode
        )
      where

    -- External imports: auxiliary
    import Data.String.Extra as S ( safeReadFile )

    -- Internal imports: auxiliary
    import Command.Result ( Result (..) )
    import Data.Location  ( Location (..) )

    -- Internal imports: C parsing and AST
    import qualified Language.C.AbsC as C ( TranslationUnit )
    import qualified Language.C.ParC as C ( myLexer, pTranslationUnit )

    -- Internal imports: transformation of C structs to handling methods.
    import qualified Language.Trans.CStructs2MsgHandlers as T ( cstructs2MsgHandlers )

    -- | Print message handlers that copy data and make it available to Copilot.
    cstructs2MsgHandlers :: FilePath -- ^ Path to a readable, valid C header file
                                     -- containing struct definitions.
                         -> IO (Result ErrorCode)
    cstructs2MsgHandlers :: String -> IO (Result ErrorCode)
    cstructs2MsgHandlers String
    fp = do
        Either String TranslationUnit
    result <- String -> IO (Either String TranslationUnit)
    parseCFile String
    fp

        case TranslationUnit -> Either String String
    T.cstructs2MsgHandlers (TranslationUnit -> Either String String)
    -> Either String TranslationUnit -> Either String String
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Either String TranslationUnit
    result of
          Right String
    content -> String -> IO ()
    putStrLn String
    content IO () -> IO (Result ErrorCode) -> IO (Result ErrorCode)
    forall a b. IO a -> IO b -> IO b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> Result ErrorCode -> IO (Result ErrorCode)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Result ErrorCode
    forall a. Result a
    Success

          Left String
    msg -> Result ErrorCode -> IO (Result ErrorCode)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Result ErrorCode -> IO (Result ErrorCode))
    -> Result ErrorCode -> IO (Result ErrorCode)
    forall a b. (a -> b) -> a -> b
    $ ErrorCode -> String -> Location -> Result ErrorCode
    forall a. a -> String -> Location -> Result a
    Error ErrorCode
    ecCStructError String
    msg (String -> Location
    LocationFile String
    fp)

      where

        -- Parse a C file, returning 'Left' with some message when there is a parse
        -- error.
        --
        parseCFile :: FilePath -> IO (Either String C.TranslationUnit)
        parseCFile :: String -> IO (Either String TranslationUnit)
    parseCFile String
    fp' = do
          Either String String
    content <- String -> IO (Either String String)
    S.safeReadFile String
    fp'
          Either String TranslationUnit -> IO (Either String TranslationUnit)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either String TranslationUnit
     -> IO (Either String TranslationUnit))
    -> Either String TranslationUnit
    -> IO (Either String TranslationUnit)
    forall a b. (a -> b) -> a -> b
    $ [Token] -> Either String TranslationUnit
    C.pTranslationUnit ([Token] -> Either String TranslationUnit)
    -> (String -> [Token]) -> String -> Either String TranslationUnit
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> [Token]
    C.myLexer (String -> Either String TranslationUnit)
    -> Either String String -> Either String TranslationUnit
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Either String String
    content

    -- * Error codes

    -- | Encoding of reasons why the command can fail.
    --
    -- The error code used is 1 for user error.
    type ErrorCode = Int

    -- | Error: the C header file cannot be read due to the file being unreadable
    -- or the format being incorrect.
    ecCStructError :: ErrorCode
    ecCStructError :: ErrorCode
    ecCStructError = ErrorCode
    1