{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}

-- | Parsing logic

module Nix.Derivation.Parser
    ( -- * Parser
      parseDerivation
    , parseDerivationWith
    , textParser
    ) where

import Data.Attoparsec.Text.Lazy (Parser)
import Data.Map (Map)
import Data.Set (Set)
import Data.Text (Text)
import Data.Vector (Vector)
import Nix.Derivation.Types (Derivation(..), DerivationOutput(..))

import qualified Data.Attoparsec.Text
import qualified Data.Attoparsec.Text.Lazy
import qualified Data.Map
import qualified Data.Set
import qualified Data.Text
import qualified Data.Vector
import qualified System.FilePath

listOf :: Parser a -> Parser [a]
listOf :: forall a. Parser a -> Parser [a]
listOf Parser a
element = do
    Parser Text Text
"["
    es <- Parser a -> Parser Text Text -> Parser Text [a]
forall (f :: * -> *) a s. Alternative f => f a -> f s -> f [a]
Data.Attoparsec.Text.Lazy.sepBy Parser a
element Parser Text Text
","
    "]"
    return es

-- | Parse a derivation
parseDerivation :: Parser (Derivation FilePath Text)
parseDerivation :: Parser (Derivation String Text)
parseDerivation = Parser String
-> Parser Text Text -> Parser (Derivation String Text)
forall fp txt.
(Ord fp, Ord txt) =>
Parser fp -> Parser txt -> Parser (Derivation fp txt)
parseDerivationWith Parser String
filepathParser Parser Text Text
textParser

-- | Parse a derivation using custom
-- parsers for filepaths and text fields
parseDerivationWith :: (Ord fp, Ord txt) => Parser fp -> Parser txt -> Parser (Derivation fp txt)
parseDerivationWith :: forall fp txt.
(Ord fp, Ord txt) =>
Parser fp -> Parser txt -> Parser (Derivation fp txt)
parseDerivationWith Parser fp
filepath Parser txt
string = do
    Parser Text Text
"Derive("

    let keyValue0 :: Parser Text (txt, DerivationOutput fp txt)
keyValue0 = do
            Parser Text Text
"("
            key <- Parser txt
string
            ","
            path <- filepath
            ","
            hashAlgo <- string
            ","
            hash <- string
            ")"
            return (key, DerivationOutput {..})
    outputs <- Parser Text (txt, DerivationOutput fp txt)
-> Parser (Map txt (DerivationOutput fp txt))
forall k v. Ord k => Parser (k, v) -> Parser (Map k v)
mapOf Parser Text (txt, DerivationOutput fp txt)
keyValue0

    ","

    let keyValue1 = do
            Parser Text Text
"("
            key <- Parser fp
filepath
            ","
            value <- setOf string
            ")"
            return (key, value)
    inputDrvs <- mapOf keyValue1

    ","

    inputSrcs <- setOf filepath

    ","

    platform <- string

    ","

    builder <- string

    ","

    args <- vectorOf string

    ","

    let keyValue2 = do
            Parser Text Text
"("
            key <- Parser txt
string
            ","
            value <- string
            ")"
            return (key, value)
    env <- mapOf keyValue2

    ")"

    return (Derivation {..})

textParser :: Parser Text
textParser :: Parser Text Text
textParser = do
    Parser Text Text
"\""

    let predicate :: Char -> Bool
predicate Char
c = Bool -> Bool
not (Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'"' Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\\')

    let loop :: Parser Text [Text]
loop = do
            text0 <- (Char -> Bool) -> Parser Text Text
Data.Attoparsec.Text.takeWhile Char -> Bool
predicate

            char0 <- Data.Attoparsec.Text.anyChar

            case char0 of
                Char
'"'  -> do
                    [Text] -> Parser Text [Text]
forall a. a -> Parser Text a
forall (m :: * -> *) a. Monad m => a -> m a
return [ Text
text0 ]

                Char
_    -> do
                    char1 <- Parser Char
Data.Attoparsec.Text.anyChar

                    char2 <- case char1 of
                        Char
'n' -> Char -> Parser Char
forall a. a -> Parser Text a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'\n'
                        Char
'r' -> Char -> Parser Char
forall a. a -> Parser Text a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'\r'
                        Char
't' -> Char -> Parser Char
forall a. a -> Parser Text a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'\t'
                        Char
_   -> Char -> Parser Char
forall a. a -> Parser Text a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
char1

                    textChunks <- loop

                    return (text0 : Data.Text.singleton char2 : textChunks)

    textChunks <- Parser Text [Text]
loop

    return (Data.Text.concat textChunks)

filepathParser :: Parser FilePath
filepathParser :: Parser String
filepathParser = do
    text <- Parser Text Text
textParser
    let str = Text -> String
Data.Text.unpack Text
text
    case (Data.Text.uncons text, System.FilePath.isValid str) of
        (Just (Char
'/', Text
_), Bool
True) -> do
            String -> Parser String
forall a. a -> Parser Text a
forall (m :: * -> *) a. Monad m => a -> m a
return String
str
        (Maybe (Char, Text), Bool)
_ -> do
            String -> Parser String
forall a. String -> Parser Text a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"bad path ‘" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
Data.Text.unpack Text
text String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"’ in derivation")

setOf :: Ord a => Parser a -> Parser (Set a)
setOf :: forall a. Ord a => Parser a -> Parser (Set a)
setOf Parser a
element = do
    es <- Parser a -> Parser [a]
forall a. Parser a -> Parser [a]
listOf Parser a
element
    return (Data.Set.fromList es)

vectorOf :: Parser a -> Parser (Vector a)
vectorOf :: forall a. Parser a -> Parser (Vector a)
vectorOf Parser a
element = do
    es <- Parser a -> Parser [a]
forall a. Parser a -> Parser [a]
listOf Parser a
element
    return (Data.Vector.fromList es)

mapOf :: Ord k => Parser (k, v) -> Parser (Map k v)
mapOf :: forall k v. Ord k => Parser (k, v) -> Parser (Map k v)
mapOf Parser (k, v)
keyValue = do
    keyValues <- Parser (k, v) -> Parser [(k, v)]
forall a. Parser a -> Parser [a]
listOf Parser (k, v)
keyValue
    return (Data.Map.fromList keyValues)