flatbuffers-0.4.0.0: src/FlatBuffers/Internal/Compiler/ParserIO.hs
{-# LANGUAGE FlexibleContexts #-}
module FlatBuffers.Internal.Compiler.ParserIO where
import Control.Monad (when)
import Control.Monad.Except (MonadError, throwError)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.State (MonadState, execStateT, get, put)
import Data.Coerce (coerce)
import Data.Foldable (traverse_)
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
import Data.Text qualified as T
import FlatBuffers.Internal.Compiler.Display (display)
import FlatBuffers.Internal.Compiler.Parser (schema)
import FlatBuffers.Internal.Compiler.SyntaxTree
(FileTree(..), Include(..), Schema, StringLiteral(..), includes)
import System.Directory qualified as Dir
import System.FilePath qualified as FP
import Text.Megaparsec (errorBundlePretty, parse)
parseSchemas ::
MonadIO m
=> MonadError String m
=> FilePath -- ^ Filepath of the root schema. It must be a path relative to the project root or an absolute path.
-> [FilePath] -- ^ Directories to search for @include@s.
-> m (FileTree Schema)
parseSchemas rootFilePath includeDirs = do
fileContent <- liftIO $ readFile rootFilePath
case parse schema rootFilePath fileContent of
Left err -> throwError $ errorBundlePretty err
Right rootSchema -> do
rootFilePathCanon <- liftIO $ Dir.canonicalizePath rootFilePath
let importedFilePaths = T.unpack . coerce <$> includes rootSchema
importedSchemas <- flip execStateT Map.empty $
traverse_
(parseImportedSchema includeDirs rootFilePathCanon)
importedFilePaths
pure FileTree
{ fileTreeFilePath = rootFilePathCanon
, fileTreeRoot = rootSchema
, fileTreeForest = importedSchemas
}
parseImportedSchema ::
MonadState (Map FilePath Schema) m
=> MonadIO m
=> MonadError String m
=> [FilePath]
-> FilePath
-> FilePath
-> m ()
parseImportedSchema includeDirs rootFilePathCanon filePath =
go rootFilePathCanon filePath
where
go parentSchemaPath filePath = do
let parentSchemaDir = FP.takeDirectory parentSchemaPath
let dirCandidates = parentSchemaDir : includeDirs
actualFilePathCanonMaybe <- liftIO $ Dir.findFile dirCandidates filePath >>= traverse Dir.canonicalizePath
case actualFilePathCanonMaybe of
Nothing -> throwError $
"File '"
<> filePath
<> "' (imported from '"
<> parentSchemaPath
<> "') not found.\nSearched in these directories: "
<> display dirCandidates
Just actualFilePathCanon -> do
importedSchemas <- get
when (actualFilePathCanon /= rootFilePathCanon && actualFilePathCanon `Map.notMember` importedSchemas) $ do
fileContent <- liftIO $ readFile actualFilePathCanon
case parse schema actualFilePathCanon fileContent of
Left err -> throwError $ errorBundlePretty err
Right importedSchema -> do
put (Map.insert actualFilePathCanon importedSchema importedSchemas)
traverse_ (go actualFilePathCanon . T.unpack . coerce) (includes importedSchema)