-- 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