{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Hie.Cabal.Parser where

import Control.Applicative
import Control.Monad
import Data.Attoparsec.Text
import Data.Char
import Data.Functor
import Data.Maybe
import Data.Text (Text)
import qualified Data.Text as T
import System.FilePath.Posix ((</>))

type Name = Text

type Path = Text

type Indent = Int

data Package = Package Name [Component]
  deriving (Int -> Package -> ShowS
[Package] -> ShowS
Package -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Package] -> ShowS
$cshowList :: [Package] -> ShowS
show :: Package -> String
$cshow :: Package -> String
showsPrec :: Int -> Package -> ShowS
$cshowsPrec :: Int -> Package -> ShowS
Show, Package -> Package -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Package -> Package -> Bool
$c/= :: Package -> Package -> Bool
== :: Package -> Package -> Bool
$c== :: Package -> Package -> Bool
Eq, Eq Package
Package -> Package -> Bool
Package -> Package -> Ordering
Package -> Package -> Package
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Package -> Package -> Package
$cmin :: Package -> Package -> Package
max :: Package -> Package -> Package
$cmax :: Package -> Package -> Package
>= :: Package -> Package -> Bool
$c>= :: Package -> Package -> Bool
> :: Package -> Package -> Bool
$c> :: Package -> Package -> Bool
<= :: Package -> Package -> Bool
$c<= :: Package -> Package -> Bool
< :: Package -> Package -> Bool
$c< :: Package -> Package -> Bool
compare :: Package -> Package -> Ordering
$ccompare :: Package -> Package -> Ordering
Ord)

data CompType = Lib | Exe | Test | Bench
  deriving (Int -> CompType -> ShowS
[CompType] -> ShowS
CompType -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CompType] -> ShowS
$cshowList :: [CompType] -> ShowS
show :: CompType -> String
$cshow :: CompType -> String
showsPrec :: Int -> CompType -> ShowS
$cshowsPrec :: Int -> CompType -> ShowS
Show, CompType -> CompType -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CompType -> CompType -> Bool
$c/= :: CompType -> CompType -> Bool
== :: CompType -> CompType -> Bool
$c== :: CompType -> CompType -> Bool
Eq, Eq CompType
CompType -> CompType -> Bool
CompType -> CompType -> Ordering
CompType -> CompType -> CompType
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: CompType -> CompType -> CompType
$cmin :: CompType -> CompType -> CompType
max :: CompType -> CompType -> CompType
$cmax :: CompType -> CompType -> CompType
>= :: CompType -> CompType -> Bool
$c>= :: CompType -> CompType -> Bool
> :: CompType -> CompType -> Bool
$c> :: CompType -> CompType -> Bool
<= :: CompType -> CompType -> Bool
$c<= :: CompType -> CompType -> Bool
< :: CompType -> CompType -> Bool
$c< :: CompType -> CompType -> Bool
compare :: CompType -> CompType -> Ordering
$ccompare :: CompType -> CompType -> Ordering
Ord)

data Component
  = Comp CompType Name Path
  deriving (Int -> Component -> ShowS
[Component] -> ShowS
Component -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Component] -> ShowS
$cshowList :: [Component] -> ShowS
show :: Component -> String
$cshow :: Component -> String
showsPrec :: Int -> Component -> ShowS
$cshowsPrec :: Int -> Component -> ShowS
Show, Component -> Component -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Component -> Component -> Bool
$c/= :: Component -> Component -> Bool
== :: Component -> Component -> Bool
$c== :: Component -> Component -> Bool
Eq, Eq Component
Component -> Component -> Bool
Component -> Component -> Ordering
Component -> Component -> Component
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Component -> Component -> Component
$cmin :: Component -> Component -> Component
max :: Component -> Component -> Component
$cmax :: Component -> Component -> Component
>= :: Component -> Component -> Bool
$c>= :: Component -> Component -> Bool
> :: Component -> Component -> Bool
$c> :: Component -> Component -> Bool
<= :: Component -> Component -> Bool
$c<= :: Component -> Component -> Bool
< :: Component -> Component -> Bool
$c< :: Component -> Component -> Bool
compare :: Component -> Component -> Ordering
$ccompare :: Component -> Component -> Ordering
Ord)

parsePackage' :: Text -> Either String Package
parsePackage' :: Text -> Either String Package
parsePackage' = forall a. Parser a -> Text -> Either String a
parseOnly Parser Package
parsePackage

-- Skip over entire fields that are known to be free-form.  Ensures lines that
-- look like the beginning of sections/stanzas are not inadvertently intepreted
-- as such.
-- List gathered by searching "free text field" in:
-- https://cabal.readthedocs.io/en/3.4/buildinfo-fields-reference.html
-- May be subject to change across Cabal versions.
skipFreeformField :: Parser ()
skipFreeformField :: Parser ()
skipFreeformField =
  forall (f :: * -> *) a. Alternative f => [f a] -> f a
choice forall a b. (a -> b) -> a -> b
$
    forall a b c. (a -> b -> c) -> b -> a -> c
flip (forall a. Int -> Text -> (Int -> Parser a) -> Parser a
field Int
0) Int -> Parser ()
skipBlock
      forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [ Text
"author",
            Text
"bug-reports",
            Text
"category",
            Text
"copyright",
            Text
"description",
            Text
"homepage",
            Text
"maintainer",
            Text
"package-url",
            Text
"stability",
            Text
"synopsis"
          ]

parsePackage :: Parser Package
parsePackage :: Parser Package
parsePackage =
  ( do
      Text
n <- forall a. Int -> Text -> (Int -> Parser a) -> Parser a
field Int
0 Text
"name" forall a b. (a -> b) -> a -> b
$ forall a b. a -> b -> a
const Parser Text Text
parseString
      (Package Text
_ [Component]
t) <- Parser Package
parsePackage
      forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Text -> [Component] -> Package
Package Text
n [Component]
t
  )
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Parser ()
skipFreeformField forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser Package
parsePackage)
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ( do
            [Component]
h <- Int -> Parser [Component]
parseComponent Int
0
            (Package Text
n [Component]
t) <- Parser Package
parsePackage
            forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Text -> [Component] -> Package
Package Text
n ([Component]
h forall a. Semigroup a => a -> a -> a
<> [Component]
t)
        )
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Parser ()
skipToNextLine forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser Package
parsePackage)
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> [Component] -> Package
Package Text
"" [])

componentHeader :: Indent -> Text -> Parser Name
componentHeader :: Int -> Text -> Parser Text Text
componentHeader Int
i Text
t = do
  Int
_ <- Int -> Parser Int
indent Int
i
  Text
_ <- Text -> Parser Text Text
asciiCI Text
t
  forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Char
tabOrSpace
  Text
n <- Parser Text Text
parseString forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
""
  Parser ()
skipToNextLine
  forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
n

parseComponent :: Indent -> Parser [Component]
parseComponent :: Int -> Parser [Component]
parseComponent Int
i =
  Int -> Parser [Component]
parseExe Int
i
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Int -> Parser [Component]
parseLib Int
i
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Int -> Parser [Component]
parseBench Int
i
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Int -> Parser [Component]
parseTestSuite Int
i

parseLib :: Indent -> Parser [Component]
parseLib :: Int -> Parser [Component]
parseLib Int
i =
  (Int -> Text -> (Text -> Text -> Component) -> Parser [Component]
parseSec Int
i Text
"library" forall a b. (a -> b) -> a -> b
$ CompType -> Text -> Text -> Component
Comp CompType
Lib)
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Int -> Text -> (Text -> Text -> Component) -> Parser [Component]
parseSec Int
i Text
"foreign-library" forall a b. (a -> b) -> a -> b
$ CompType -> Text -> Text -> Component
Comp CompType
Lib)

parseTestSuite :: Indent -> Parser [Component]
parseTestSuite :: Int -> Parser [Component]
parseTestSuite Int
i = Int -> Text -> (Text -> Text -> Component) -> Parser [Component]
parseSec Int
i Text
"test-suite" forall a b. (a -> b) -> a -> b
$ CompType -> Text -> Text -> Component
Comp CompType
Test

parseExe :: Indent -> Parser [Component]
parseExe :: Int -> Parser [Component]
parseExe = (Text -> Text -> Component) -> Text -> Int -> Parser [Component]
parseSecMain (CompType -> Text -> Text -> Component
Comp CompType
Exe) Text
"executable"

parseBench :: Indent -> Parser [Component]
parseBench :: Int -> Parser [Component]
parseBench = (Text -> Text -> Component) -> Text -> Int -> Parser [Component]
parseSecMain (CompType -> Text -> Text -> Component
Comp CompType
Bench) Text
"benchmark"

parseSecMain :: (Name -> Path -> Component) -> Text -> Indent -> Parser [Component]
parseSecMain :: (Text -> Text -> Component) -> Text -> Int -> Parser [Component]
parseSecMain Text -> Text -> Component
c Text
s Int
i = do
  Text
n <- Int -> Text -> Parser Text Text
componentHeader Int
i Text
s
  [Text]
p <- Int -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]
pathMain (Int
i forall a. Num a => a -> a -> a
+ Int
1) [Text
"./"] Text
"" [] []
  forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (Text -> Text -> Component
c Text
n) [Text]
p

parseQuoted :: Parser Text
parseQuoted :: Parser Text Text
parseQuoted = do
  Char
q <- Char -> Parser Char
char Char
'"' forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Char
char Char
'\''
  Text
s <- (Char -> Bool) -> Parser Text Text
takeTill (forall a. Eq a => a -> a -> Bool
== Char
q)
  Char
_ <- Char -> Parser Char
char Char
q
  forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
s

parseString :: Parser Name
parseString :: Parser Text Text
parseString = Parser Text Text
parseQuoted forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Text
unqualName

unqualName :: Parser Text
unqualName :: Parser Text Text
unqualName = (Char -> Bool) -> Parser Text Text
takeWhile1 (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\Char
c -> Char -> Bool
isSpace Char
c Bool -> Bool -> Bool
|| Char
c forall a. Eq a => a -> a -> Bool
== Char
','))

-- | Comma or space separated list, with optional new lines.
parseList :: Indent -> Parser [Text]
parseList :: Int -> Parser [Text]
parseList Int
i = forall (f :: * -> *) a. Alternative f => f a -> f [a]
many (Parser Text Text
nl forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Text
sl)
  where
    sep :: Parser ()
sep = forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany (Char -> Parser Char
char Char
',' forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Char
tabOrSpace)
    com :: Parser ()
com = forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Char
tabOrSpace forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser Text Text
"--" forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> (Char -> Bool) -> Parser ()
skipWhile (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isEndOfLine)
    sl :: Parser Text Text
sl = do
      Parser ()
sep
      Text
x <- Parser Text Text
parseString
      Parser ()
sep
      forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser ()
com
      forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
x

    nl :: Parser Text Text
nl = do
      forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser ()
emptyOrComLine forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser ()
endOfLine
      Int
_ <- Int -> Parser Int
indent Int
i
      Parser ()
sep
      forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser ()
com
      Text
x <- Parser Text Text
parseString
      Parser ()
sep
      forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser ()
com
      forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
x

pathMain :: Indent -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]
pathMain :: Int -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]
pathMain Int
i [Text]
p Text
m [Text]
o [Text]
a =
  (Int -> Parser [Text]
hsSourceDir Int
i forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\[Text]
p' -> Int -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]
pathMain Int
i [Text]
p' Text
m [Text]
o [Text]
a))
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (forall a. Int -> Text -> (Int -> Parser a) -> Parser a
field Int
i Text
"main-is" (forall a b. a -> b -> a
const Parser Text Text
parseString) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\Text
m' -> Int -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]
pathMain Int
i [Text]
p Text
m' [Text]
o [Text]
a))
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (forall a. Int -> Text -> (Int -> Parser a) -> Parser a
field Int
i Text
"other-modules" Int -> Parser [Text]
parseList forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a b c. (a -> b -> c) -> b -> a -> c
flip (Int -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]
pathMain Int
i [Text]
p Text
m) [Text]
a)
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (forall a. Int -> Text -> (Int -> Parser a) -> Parser a
field Int
i Text
"autogen-modules" Int -> Parser [Text]
parseList forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Int -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]
pathMain Int
i [Text]
p Text
m [Text]
o)
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Int -> Parser ()
skipBlockLine Int
i forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Int -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]
pathMain Int
i [Text]
p Text
m [Text]
o [Text]
a)
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (f :: * -> *) a. Applicative f => a -> f a
pure
      ( forall a b. (a -> b) -> [a] -> [b]
map (Text -> Text -> Text
<//> Text
m) [Text]
p
          forall a. Semigroup a => a -> a -> a
<> [ Text
p' Text -> Text -> Text
<//> (Text
o'' forall a. Semigroup a => a -> a -> a
<> Text
".hs")
               | Text
p' <- [Text]
p,
                 Text
o' <- forall a. (a -> Bool) -> [a] -> [a]
filter (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [Text]
a) [Text]
o,
                 let o'' :: Text
o'' = Text -> Text -> Text -> Text
T.replace Text
"." Text
"/" Text
o'
             ]
      )

(<//>) :: Text -> Text -> Text
Text
a <//> :: Text -> Text -> Text
<//> Text
b = String -> Text
T.pack (Text -> String
T.unpack Text
a String -> ShowS
</> Text -> String
T.unpack Text
b)

infixr 5 <//>

parseSec :: Indent -> Text -> (Name -> Path -> Component) -> Parser [Component]
parseSec :: Int -> Text -> (Text -> Text -> Component) -> Parser [Component]
parseSec Int
i Text
compType Text -> Text -> Component
compCon = do
  Text
n <- Int -> Text -> Parser Text Text
componentHeader Int
i Text
compType
  [Text]
p <- Int -> [Text] -> Parser [Text]
extractPath (Int
i forall a. Num a => a -> a -> a
+ Int
1) []
  let p' :: [Text]
p' = if forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Text]
p then [Text
"./"] else [Text]
p
  forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (Text -> Text -> Component
compCon Text
n) [Text]
p'

skipToNextLine :: Parser ()
skipToNextLine :: Parser ()
skipToNextLine = (Char -> Bool) -> Parser ()
skipWhile (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isEndOfLine) forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser ()
endOfLine

skipBlock :: Indent -> Parser ()
skipBlock :: Int -> Parser ()
skipBlock Int
i = forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany forall a b. (a -> b) -> a -> b
$ Int -> Parser ()
skipBlockLine Int
i

comment :: Parser ()
comment :: Parser ()
comment = forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Char
tabOrSpace forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser Text Text
"--" forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser ()
skipToNextLine

skipBlockLine :: Indent -> Parser ()
skipBlockLine :: Int -> Parser ()
skipBlockLine Int
i = (Int -> Parser Int
indent Int
i forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser ()
skipToNextLine) forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser ()
emptyOrComLine

emptyOrComLine :: Parser ()
emptyOrComLine :: Parser ()
emptyOrComLine = (forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Char
tabOrSpace forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser ()
endOfLine) forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser ()
comment

tabOrSpace :: Parser Char
tabOrSpace :: Parser Char
tabOrSpace = Char -> Parser Char
char Char
' ' forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Char
char Char
'\t'

hsSourceDir :: Indent -> Parser [Text]
hsSourceDir :: Int -> Parser [Text]
hsSourceDir Int
i = forall a. Int -> Text -> (Int -> Parser a) -> Parser a
field Int
i Text
"hs-source-dirs" Int -> Parser [Text]
parseList

-- field :: Indent -> Text -> Parser Text
field ::
  Indent ->
  Text ->
  (Indent -> Parser a) ->
  Parser a
field :: forall a. Int -> Text -> (Int -> Parser a) -> Parser a
field Int
i Text
f Int -> Parser a
p =
  do
    Int
i' <- Int -> Parser Int
indent Int
i
    Text
_ <- Text -> Parser Text Text
asciiCI Text
f
    forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Char
tabOrSpace
    Char
_ <- Char -> Parser Char
char Char
':'
    forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Char
tabOrSpace
    a
p' <- Int -> Parser a
p forall a b. (a -> b) -> a -> b
$ Int
i' forall a. Num a => a -> a -> a
+ Int
1
    Parser ()
skipToNextLine
    forall (f :: * -> *) a. Applicative f => a -> f a
pure a
p'

extractPath :: Indent -> [Path] -> Parser [Path]
extractPath :: Int -> [Text] -> Parser [Text]
extractPath Int
i [Text]
ps =
  (forall a. Int -> Text -> (Int -> Parser a) -> Parser a
field Int
i Text
"hs-source-dirs" Int -> Parser [Text]
parseList forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\[Text]
p -> Int -> [Text] -> Parser [Text]
extractPath Int
i forall a b. (a -> b) -> a -> b
$ [Text]
ps forall a. Semigroup a => a -> a -> a
<> [Text]
p))
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Int -> Parser ()
skipBlockLine Int
i forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Int -> [Text] -> Parser [Text]
extractPath Int
i [Text]
ps)
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Parser ()
comment forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Int -> [Text] -> Parser [Text]
extractPath Int
i [Text]
ps)
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (f :: * -> *) a. Applicative f => a -> f a
pure [Text]
ps

-- | Skip at least n spaces
indent :: Indent -> Parser Int
indent :: Int -> Parser Int
indent Int
i = do
  Int
c <- forall (t :: * -> *) a. Foldable t => t a -> Int
length forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
many' Parser Char
tabOrSpace
  if Int
c forall a. Ord a => a -> a -> Bool
>= Int
i then forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
c else forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"insufficient indent"

extractPkgs :: Parser [T.Text]
extractPkgs :: Parser [Text]
extractPkgs = forall (m :: * -> *) a. Monad m => m (m a) -> m a
join forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [Maybe a] -> [a]
catMaybes forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
many' (forall a. a -> Maybe a
Just forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Int -> Text -> (Int -> Parser a) -> Parser a
field Int
0 Text
"packages" Int -> Parser [Text]
parseList forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Parser ()
skipToNextLine forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing))