packages feed

cabal-gild-0.2.1.1: source/library/CabalGild/Pragma.hs

{-# LANGUAGE OverloadedStrings #-}

module CabalGild.Pragma where

import CabalGild.Comments
import CabalGild.Glob
import qualified Data.Bifunctor as Bifunctor
import qualified Data.ByteString as BS
import qualified Data.Either as Either
import qualified Data.Maybe as Maybe
import qualified Distribution.Compat.CharParsing as C
import qualified Distribution.ModuleName as C
import qualified Distribution.Parsec as C
import qualified Distribution.Parsec.FieldLineStream as C

-------------------------------------------------------------------------------
-- Types
-------------------------------------------------------------------------------

data Pragma
  = FieldPragma FieldPragma
  | GlobalPragma GlobalPragma
  deriving (Show)

-- | Pragmas applied per field
data FieldPragma
  = PragmaExpandModules FilePath [C.ModuleName]
  | PragmaGlobFiles Glob
  | PragmaFragment FilePath
  deriving (Show)

-- | Pragmas affecting global output
data GlobalPragma
  = PragmaOptIndent Int
  | PragmaOptTabular Bool
  deriving (Show)

-------------------------------------------------------------------------------
-- Parser
-------------------------------------------------------------------------------

-- | Parse pragma from 'ByteString'.
--
-- An error ('Left') is reported only if input 'ByteString' starts with @-- cabal-gild:@.
parsePragma :: BS.ByteString -> Either String (Maybe Pragma)
parsePragma bs = case dropPrefix bs of
  Nothing -> Right Nothing
  Just bs' -> Bifunctor.bimap show Just $ C.runParsecParser parser "<input>" $ C.fieldLineStreamFromBS bs'
  where
    dropPrefix bs0 = do
      bs1 <- BS.stripPrefix "--" bs0
      bs2 <- BS.stripPrefix "cabal-gild:" (stripWhitespace bs1)
      return (stripWhitespace bs2)

    parser :: C.ParsecParser Pragma
    parser = do
      t <- C.parsecToken
      case t of
        "expand" -> expandModules
        "indent" -> indent
        "glob-files" -> globFiles
        "tabular" -> return $ GlobalPragma $ PragmaOptTabular True
        "no-tabular" -> return $ GlobalPragma $ PragmaOptTabular False
        "fragment" -> fragment
        _ -> fail $ "Unknown pragma " ++ t

    expandModules :: C.ParsecParser Pragma
    expandModules = do
      C.spaces
      dir <- C.parsecToken
      mns <- C.many (C.space *> C.spaces *> C.char '-' *> C.parsec)
      return $ FieldPragma $ PragmaExpandModules dir mns

    indent :: C.ParsecParser Pragma
    indent = do
      C.spaces
      GlobalPragma . PragmaOptIndent <$> C.integral

    fragment :: C.ParsecParser Pragma
    fragment = do
      C.spaces
      FieldPragma . PragmaFragment <$> C.parsecToken

    globFiles :: C.ParsecParser Pragma
    globFiles = do
      C.spaces
      t <- C.parsecToken
      case parseGlob t of
        Right g -> return $ FieldPragma $ PragmaGlobFiles g
        Left e -> C.unexpected e

stripWhitespace :: BS.ByteString -> BS.ByteString
stripWhitespace bs = case BS.uncons bs of
  Nothing -> bs
  Just (w, bs')
    | w == 32 -> stripWhitespace bs'
    | otherwise -> bs

parsePragmas :: Comments -> ([String], [Pragma])
parsePragmas = fmap Maybe.catMaybes . Either.partitionEithers . map parsePragma . unComments