packages feed

descript-lang-0.2.0.0: src/Descript/Misc/Build/Read/File/SFile.hs

{-# LANGUAGE TypeFamilies #-}

module Descript.Misc.Build.Read.File.SFile
  ( SFile (..)
  , FileSummary (..)
  , mkSFile
  , loadSFile
  , ifile
  , defaultFileScope
  ) where

import Descript.Misc.Build.Read.File.Scope
import Descript.Misc.Error
import Descript.Misc.Summary
import qualified Text.Megaparsec.Error as Parsec
import Text.Megaparsec.Error hiding (ParseError)
import Data.Text (Text)
import qualified Data.Text.IO as Text
import System.FilePath

-- | A self-contained source file.
data SFile
  = SFile
  { sfileName :: String
  , sfileContents :: Text
  } deriving (Eq, Ord, Read, Show)

-- | Can get a summary of an instance given the file it came from.
class FileSummary a where
  -- A summary of the value, given the file it came from.
  summaryF :: SFile -> a -> String

instance (FileSummary e, Summary a) => FileSummary (Result e a) where
  summaryF file (Failure err) = "Failure: " ++ summaryF file err
  summaryF _ (Success val) = summary val

instance (ShowErrorComponent e, t ~ Char) => FileSummary (Parsec.ParseError t e) where
  summaryF file err = parseErrorPretty' (sfileContents file) err

-- | Creates a file at the given path.
mkSFile :: FilePath -> Text -> SFile
mkSFile path contents
 = SFile
 { sfileName = takeBaseName path
 , sfileContents = contents
 }

-- | Reads the file with the given path.
loadSFile :: FilePath -> IO SFile
loadSFile path = mkSFile path <$> Text.readFile path

-- | Creates a file for text in "interactive mode" (e.g. from a
-- console).
ifile :: Text -> SFile
ifile contents
  = SFile
  { sfileName = ifileName
  , sfileContents = contents
  }

-- | The scope of the module in the file, if the module doesn't have an
-- explicit scope.
defaultFileScope :: SFile -> AbsScope
defaultFileScope = AbsScope . pure . sfileName

ifileName :: String
ifileName = "<interactive>"