packages feed

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

{-# LANGUAGE OverloadedStrings #-}

module CabalGild.Pragma where

import CabalGild.Comments
import CabalGild.Glob
import CabalGild.Prelude
import qualified Data.ByteString as BS
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 :: ByteString -> Either String (Maybe Pragma)
parsePragma bs = case dropPrefix bs of
  Nothing -> Right Nothing
  Just bs' -> 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 :: ByteString -> 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 catMaybes . partitionEithers . map parsePragma . unComments