    -- Copyright 2024 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.
    --
    -- | Parsing of diagrams.
    module Data.Diagram.Parser
        ( DiagramFormat(..)
        , readDiagram
        )
      where

    -- External imports
    import           Control.Monad.Except (ExceptT (..))
    import qualified Data.ByteString.Lazy as B

    -- External imports: auxiliary
    import Data.ByteString.Extra as B (safeReadFile)

    -- Internal imports: auxiliary
    import Command.Errors              (ErrorCode, ErrorTriplet (..))
    import Data.Diagram                (Diagram)
    import Data.Diagram.Parser.Dot     (parseDiagramDot)
    import Data.Diagram.Parser.Mermaid (parseDiagramMermaid)
    import Data.Either.Extra           (mapLeft)
    import Data.ExprPair               (ExprPair)
    import Data.Location               (Location (..))

    -- | Diagram formats supported.
    data DiagramFormat = Mermaid
                       | Dot
      deriving (DiagramFormat -> DiagramFormat -> Bool
    (DiagramFormat -> DiagramFormat -> Bool)
    -> (DiagramFormat -> DiagramFormat -> Bool) -> Eq DiagramFormat
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: DiagramFormat -> DiagramFormat -> Bool
    == :: DiagramFormat -> DiagramFormat -> Bool
    $c/= :: DiagramFormat -> DiagramFormat -> Bool
    /= :: DiagramFormat -> DiagramFormat -> Bool
    Eq, Int -> DiagramFormat -> ShowS
    [DiagramFormat] -> ShowS
    DiagramFormat -> String
    (Int -> DiagramFormat -> ShowS)
    -> (DiagramFormat -> String)
    -> ([DiagramFormat] -> ShowS)
    -> Show DiagramFormat
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> DiagramFormat -> ShowS
    showsPrec :: Int -> DiagramFormat -> ShowS
    $cshow :: DiagramFormat -> String
    show :: DiagramFormat -> String
    $cshowList :: [DiagramFormat] -> ShowS
    showList :: [DiagramFormat] -> ShowS
    Show)

    -- | Read a diagram from a file.
    readDiagram :: FilePath                         -- ^ File containing diagram
                -> DiagramFormat                    -- ^ Format of the input file
                -> ExprPair                         -- ^ Subparser for conditions
                                                    -- or edge expressions
                -> ExceptT ErrorTriplet IO Diagram
    readDiagram :: String
    -> DiagramFormat -> ExprPair -> ExceptT ErrorTriplet IO Diagram
    readDiagram String
    fp DiagramFormat
    format ExprPair
    exprP = IO (Either ErrorTriplet Diagram) -> ExceptT ErrorTriplet IO Diagram
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT (IO (Either ErrorTriplet Diagram)
     -> ExceptT ErrorTriplet IO Diagram)
    -> IO (Either ErrorTriplet Diagram)
    -> ExceptT ErrorTriplet IO Diagram
    forall a b. (a -> b) -> a -> b
    $ do
      contentEither <- String -> IO (Either String ByteString)
    B.safeReadFile String
    fp
      let diagramE = do
            -- The following functions use Either to return errors. The use of
            -- (>>=) to chain functions makes the program stop at the earliest
            -- error.
            diagFileContent <- Either String ByteString
    contentEither

            -- Abtract representation of a state machine diagram.
            parseDiagram format diagFileContent exprP

      pure $ mapLeft
               (\String
    msg -> Int -> String -> Location -> ErrorTriplet
    ErrorTriplet Int
    ecCannotReadDiagram String
    msg (String -> Location
    LocationFile String
    fp))
               diagramE

    --- | Generic function to parse a diagram.
    parseDiagram :: DiagramFormat          -- ^ Format of the input file
                 -> B.ByteString           -- ^ Contents of the diagram
                 -> ExprPair               -- ^ Subparser for conditions or edge
                                           -- expressions
                 -> Either String Diagram
    parseDiagram :: DiagramFormat -> ByteString -> ExprPair -> Either String Diagram
    parseDiagram DiagramFormat
    Dot     = ByteString -> ExprPair -> Either String Diagram
    parseDiagramDot
    parseDiagram DiagramFormat
    Mermaid = ByteString -> ExprPair -> Either String Diagram
    parseDiagramMermaid

    -- ** Error codes

    -- | Error: the diagram cannot be read due to an error opening the file or
    -- parsing the file.
    ecCannotReadDiagram :: ErrorCode
    ecCannotReadDiagram :: Int
    ecCannotReadDiagram = Int
    1