{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings  #-}
{-# LANGUAGE QuasiQuotes        #-}
{-# LANGUAGE ViewPatterns       #-}

{-| This library exports two utilities for compiling Dhall expressions to Bash:

    * `dhallToExpression`, which emits a Bash expression (i.e. a valid
      right-hand side for an assignment)

    * `dhallToStatement`, which emits a Bash @declare@ or @unset@ statement
      suitable for use with `eval`

    `dhallToExpression` only supports the conversion of primitive values, such
    as:

    * @Bool@ - which translates to a string that is either @"true"@ or @"false"@
    * @Natural@ - which translates to a Bash integer
    * @Integer@ - which translates to a Bash integer
    * @Text@ - which translates to a Bash string (properly escaped if necessary)

    The @dhall-to-bash@ executable by default tries to compile Dhall expressions
    to Bash expressions using the `dhallToExpression` function.  For example:

> $ dhall-to-bash <<< 'True'
> true
> $ dhall-to-bash <<< 'False'
> false
> $ dhall-to-bash <<< '1'
> 1
> $ dhall-to-bash <<< '+1'
> 1
> $ dhall-to-bash <<< '"ABC"'
> ABC
> $ dhall-to-bash <<< '" X "'
> $' X '
> $ dhall-to-bash <<< 'Natural/even +100'
> true

    The output of `dhallToExpression` is a valid Bash expression that can be
    embedded anywhere Bash expressions are valid, such as the right-hand side of
    an assignment statement:

> $ FOO=$(dhall-to-bash <<< 'List/length Natural [1, 2, 3]')
> $ echo "${FOO}"
> 3

    `dhallToStatement` supports a wider range of expressions by also adding
    support for:

    * @Optional@ - which translates to a variable which is either set or unset
    * @List@ - which translates to a Bash array
    * records - which translate to Bash associative arrays

    The @dhall-to-bash@ executable can emit a statement instead of an expression
    if you add the @--declare@ flag specifying which variable to set or unset.
    For example:

> $ dhall-to-bash --declare FOO <<< 'None Natural'
> unset FOO
> $ dhall-to-bash --declare FOO <<< 'Some 1'
> declare -r -i FOO=1
> $ dhall-to-bash --declare FOO <<< 'Some (Some 1)'
> declare -r -i FOO=1
> $ dhall-to-bash --declare FOO <<< 'Some (None Natural)'
> unset FOO
> $ dhall-to-bash --declare FOO <<< '[1, 2, 3]'
> declare -r -a FOO=(1 2 3)
> $ dhall-to-bash --declare FOO <<< '{ bar = 1, baz = True }'
> declare -r -A FOO=([bar]=1 [baz]=true)

    The output of `dhallToExpression` is either a @declare@ or @unset@ Bash
    statement that you can pass to @eval@:

> $ eval $(dhall-to-bash --declare FOO <<< '{ bar = 1, baz = True }')
> $ echo "${FOO[bar]}"
> 1
> $ echo "${FOO[baz]}"
> true

    @dhall-to-bash@ declares variables read-only (i.e. @-r@) to prevent you from
    accidentally overwriting, deleting or mutating variables:

> $ eval $(dist/build/dhall-to-bash/dhall-to-bash --declare BAR <<< '1')
> $ echo "${BAR"}
> 1
> $ unset BAR
> bash: unset: BAR: cannot unset: readonly variable
> $ eval $(dist/build/dhall-to-bash/dhall-to-bash --declare BAR <<< '2')
> bash: declare: BAR: readonly variable

-}

module Dhall.Bash (
    -- * Dhall to Bash
      dhallToExpression
    , dhallToStatement

    -- * Exceptions
    , ExpressionError(..)
    , StatementError(..)
    ) where

import Control.Exception (Exception)
import Data.Bifunctor    (first)
import Data.ByteString
import Data.Typeable     (Typeable)
import Data.Void         (Void, absurd)
import Dhall.Core        (Chunks (..), Expr (..))

import qualified Data.Foldable
import qualified Data.Text
import qualified Data.Text.Encoding
import qualified Dhall.Core
import qualified Dhall.Map
import qualified NeatInterpolation
import qualified Text.ShellEscape

_ERROR :: Data.Text.Text
_ERROR :: Text
_ERROR = Text
"\ESC[1;31mError\ESC[0m"

{-| This is the exception type for errors that might arise when translating
    Dhall expressions to Bash statements

    Because the majority of Dhall language features do not easily translate to
    Bash this just returns the expression that failed
-}
data StatementError
    = UnsupportedStatement (Expr Void Void)
    | UnsupportedSubexpression (Expr Void Void)
    deriving (Typeable)

instance Show StatementError where
    show :: StatementError -> String
show (UnsupportedStatement Expr Void Void
e) =
        Text -> String
Data.Text.unpack [NeatInterpolation.text|
$_ERROR: Cannot translate to a Bash statement

Explanation: Only primitive values, records, ❰List❱s, and ❰Optional❱ values can
be translated from Dhall to a Bash statement

The following Dhall expression could not be translated to a Bash statement:

↳ $txt
|]
      where
        txt :: Text
txt = Expr Void Void -> Text
forall a. Pretty a => a -> Text
Dhall.Core.pretty Expr Void Void
e

    show (UnsupportedSubexpression Expr Void Void
e) =
        -- Carefully note: No tip suggesting `--declare` since it won't work
        -- here (and the user is already using `--declare`)
        Text -> String
Data.Text.unpack [NeatInterpolation.text|
$_ERROR: Cannot translate to a Bash expression

Explanation: Only primitive values can be translated from Dhall to a Bash
expression

The following Dhall expression could not be translated to a Bash expression:

↳ $txt
|]
      where
        txt :: Text
txt = Expr Void Void -> Text
forall a. Pretty a => a -> Text
Dhall.Core.pretty Expr Void Void
e

instance Exception StatementError

{-| This is the exception type for errors that might arise when translating
    Dhall expressions to Bash expressions

    Because the majority of Dhall language features do not easily translate to
    Bash this just returns the expression that failed
-}
data ExpressionError = UnsupportedExpression (Expr Void Void) deriving (Typeable)

instance Show ExpressionError where
    show :: ExpressionError -> String
show (UnsupportedExpression Expr Void Void
e) =
        Text -> String
Data.Text.unpack [NeatInterpolation.text|
$_ERROR: Cannot translate to a Bash expression

Explanation: Only primitive values can be translated from Dhall to a Bash
expression

The following Dhall expression could not be translated to a Bash expression:

↳ $txt$tip
|]
      where
        txt :: Text
txt = Expr Void Void -> Text
forall a. Pretty a => a -> Text
Dhall.Core.pretty Expr Void Void
e

        tip :: Text
tip = case Expr Void Void
e of
            Some Expr Void Void
_ -> Text
"\n\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [NeatInterpolation.text|
Tip: You can convert an ❰Optional❱ value to a Bash statement using the --declare
flag
|]
            ListLit Maybe (Expr Void Void)
_ Seq (Expr Void Void)
_ -> Text
"\n\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [NeatInterpolation.text|
Tip: You can convert a ❰List❱ to a Bash statement using the --declare flag
|]
            RecordLit Map Text (RecordField Void Void)
_ -> Text
"\n\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [NeatInterpolation.text|
Tip: You can convert a record to a Bash statement using the --declare flag
|]
            Expr Void Void
_ -> Text
""

instance Exception ExpressionError

{-| Compile a Dhall expression to a Bash statement that @declare@s or @unset@s a
    a variable of your choice

    This only supports:

    * @Bool@s
    * @Natural@s
    * @Integer@s
    * @Text@s
    * @Optional@s
    * @List@s
    * records
 -}
dhallToStatement
    :: Expr s Void
    -- ^ Dhall expression to compile
    -> ByteString
    -- ^ Variable to @declare@ or @unset@
    -> Either StatementError ByteString
    -- ^ Bash statement or compile failure
dhallToStatement :: Expr s Void -> ByteString -> Either StatementError ByteString
dhallToStatement Expr s Void
expr0 ByteString
var0 = Expr Void Void -> Either StatementError ByteString
go (Expr s Void -> Expr Void Void
forall a s t. Eq a => Expr s a -> Expr t a
Dhall.Core.normalize Expr s Void
expr0)
  where
    var :: ByteString
var = Bash -> ByteString
forall t. Escape t => t -> ByteString
Text.ShellEscape.bytes (ByteString -> Bash
Text.ShellEscape.bash ByteString
var0)

    adapt :: ExpressionError -> StatementError
adapt (UnsupportedExpression Expr Void Void
e) = Expr Void Void -> StatementError
UnsupportedSubexpression Expr Void Void
e

    go :: Expr Void Void -> Either StatementError ByteString
go (BoolLit Bool
a) =
        Expr Void Void -> Either StatementError ByteString
go (Chunks Void Void -> Expr Void Void
forall s a. Chunks s a -> Expr s a
TextLit (if Bool
a then Chunks Void Void
"true" else Chunks Void Void
"false"))
    go (NaturalLit Natural
a) =
        Expr Void Void -> Either StatementError ByteString
go (Integer -> Expr Void Void
forall s a. Integer -> Expr s a
IntegerLit (Natural -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Natural
a))
    go (IntegerLit Integer
a) = do
        ByteString
e <- (ExpressionError -> StatementError)
-> Either ExpressionError ByteString
-> Either StatementError ByteString
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ExpressionError -> StatementError
adapt (Expr Any Void -> Either ExpressionError ByteString
forall s. Expr s Void -> Either ExpressionError ByteString
dhallToExpression (Integer -> Expr Any Void
forall s a. Integer -> Expr s a
IntegerLit Integer
a))
        let bytes :: ByteString
bytes = ByteString
"declare -r -i " ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
var ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
"=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
e
        ByteString -> Either StatementError ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
bytes
    go (TextLit Chunks Void Void
a) = do
        ByteString
e <- (ExpressionError -> StatementError)
-> Either ExpressionError ByteString
-> Either StatementError ByteString
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ExpressionError -> StatementError
adapt (Expr Void Void -> Either ExpressionError ByteString
forall s. Expr s Void -> Either ExpressionError ByteString
dhallToExpression (Chunks Void Void -> Expr Void Void
forall s a. Chunks s a -> Expr s a
TextLit Chunks Void Void
a))
        let bytes :: ByteString
bytes = ByteString
"declare -r " ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
var ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
"=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
e
        ByteString -> Either StatementError ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
bytes
    go (ListLit Maybe (Expr Void Void)
_ Seq (Expr Void Void)
bs) = do
        Seq ByteString
bs' <- (ExpressionError -> StatementError)
-> Either ExpressionError (Seq ByteString)
-> Either StatementError (Seq ByteString)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ExpressionError -> StatementError
adapt ((Expr Void Void -> Either ExpressionError ByteString)
-> Seq (Expr Void Void) -> Either ExpressionError (Seq ByteString)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Expr Void Void -> Either ExpressionError ByteString
forall s. Expr s Void -> Either ExpressionError ByteString
dhallToExpression Seq (Expr Void Void)
bs)
        let bytes :: ByteString
bytes
                =   ByteString
"declare -r -a "
                ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<>  ByteString
var
                ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<>  ByteString
"=("
                ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<>  ByteString -> [ByteString] -> ByteString
Data.ByteString.intercalate ByteString
" " (Seq ByteString -> [ByteString]
forall (t :: * -> *) a. Foldable t => t a -> [a]
Data.Foldable.toList Seq ByteString
bs')
                ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<>  ByteString
")"
        ByteString -> Either StatementError ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
bytes
    go (Some Expr Void Void
b) = Expr Void Void -> Either StatementError ByteString
go Expr Void Void
b
    go (App Expr Void Void
None Expr Void Void
_) = ByteString -> Either StatementError ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString
"unset " ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
var)
    go (RecordLit Map Text (RecordField Void Void)
a) = do
        let process :: (Text, Expr s Void) -> Either ExpressionError ByteString
process (Text
k, Expr s Void
v) = do
                ByteString
v' <- Expr s Void -> Either ExpressionError ByteString
forall s. Expr s Void -> Either ExpressionError ByteString
dhallToExpression Expr s Void
v
                let bytes :: ByteString
bytes = Text -> ByteString
Data.Text.Encoding.encodeUtf8 Text
k
                let k' :: ByteString
k'    = Bash -> ByteString
forall t. Escape t => t -> ByteString
Text.ShellEscape.bytes (ByteString -> Bash
Text.ShellEscape.bash ByteString
bytes)
                ByteString -> Either ExpressionError ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString
"[" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
k' ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
"]=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
v')
        [ByteString]
kvs' <- (ExpressionError -> StatementError)
-> Either ExpressionError [ByteString]
-> Either StatementError [ByteString]
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ExpressionError -> StatementError
adapt (((Text, Expr Void Void) -> Either ExpressionError ByteString)
-> [(Text, Expr Void Void)] -> Either ExpressionError [ByteString]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse (Text, Expr Void Void) -> Either ExpressionError ByteString
forall s. (Text, Expr s Void) -> Either ExpressionError ByteString
process (Map Text (Expr Void Void) -> [(Text, Expr Void Void)]
forall k v. Ord k => Map k v -> [(k, v)]
Dhall.Map.toList (Map Text (Expr Void Void) -> [(Text, Expr Void Void)])
-> Map Text (Expr Void Void) -> [(Text, Expr Void Void)]
forall a b. (a -> b) -> a -> b
$ RecordField Void Void -> Expr Void Void
forall s a. RecordField s a -> Expr s a
Dhall.Core.recordFieldValue (RecordField Void Void -> Expr Void Void)
-> Map Text (RecordField Void Void) -> Map Text (Expr Void Void)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Map Text (RecordField Void Void)
a))
        let bytes :: ByteString
bytes
                =   ByteString
"declare -r -A "
                ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<>  ByteString
var
                ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<>  ByteString
"=("
                ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<>  ByteString -> [ByteString] -> ByteString
Data.ByteString.intercalate ByteString
" " [ByteString]
kvs'
                ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<>  ByteString
")"
        ByteString -> Either StatementError ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
bytes
    go (Field (Union Map Text (Maybe (Expr Void Void))
m) FieldSelection Void
k) = do
        ByteString
e <- (ExpressionError -> StatementError)
-> Either ExpressionError ByteString
-> Either StatementError ByteString
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ExpressionError -> StatementError
adapt (Expr Void Void -> Either ExpressionError ByteString
forall s. Expr s Void -> Either ExpressionError ByteString
dhallToExpression (Expr Void Void -> FieldSelection Void -> Expr Void Void
forall s a. Expr s a -> FieldSelection s -> Expr s a
Field (Map Text (Maybe (Expr Void Void)) -> Expr Void Void
forall s a. Map Text (Maybe (Expr s a)) -> Expr s a
Union Map Text (Maybe (Expr Void Void))
m) FieldSelection Void
k))
        let bytes :: ByteString
bytes = ByteString
"declare -r " ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
var ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
"=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
e
        ByteString -> Either StatementError ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
bytes
    go (Embed   Void
x) =
        Void -> Either StatementError ByteString
forall a. Void -> a
absurd Void
x
    go (Note  Void
_ Expr Void Void
e) =
        Expr Void Void -> Either StatementError ByteString
go Expr Void Void
e

    -- Use an exhaustive pattern match here so that we don't forget to handle
    -- new constructors added to the API
    go e :: Expr Void Void
e@(Const            {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Var              {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Lam              {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Pi               {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(App              {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Let              {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Annot            {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Bool             {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(BoolAnd          {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(BoolOr           {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(BoolEQ           {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(BoolNE           {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(BoolIf           {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
Natural            ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
NaturalFold        ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
NaturalBuild       ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
NaturalIsZero      ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
NaturalEven        ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
NaturalOdd         ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
NaturalToInteger   ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
NaturalShow        ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
NaturalSubtract    ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(NaturalPlus      {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(NaturalTimes     {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
Integer            ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
IntegerClamp       ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
IntegerNegate      ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
IntegerShow        ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
IntegerToDouble    ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
Double             ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(DoubleLit        {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
DoubleShow         ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
Text               ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(TextAppend       {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(TextReplace      {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(TextShow         {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
List               ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(ListAppend       {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
ListBuild          ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
ListFold           ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
ListLength         ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
ListHead           ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
ListLast           ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
ListIndexed        ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
ListReverse        ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
Optional           ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Expr Void Void
None               ) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Record           {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Union            {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Combine          {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(CombineTypes     {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Prefer           {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(RecordCompletion {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Merge            {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(ToMap            {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Field            {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Project          {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Assert           {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(Equivalent       {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(With             {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)
    go e :: Expr Void Void
e@(ImportAlt        {}) = StatementError -> Either StatementError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> StatementError
UnsupportedStatement Expr Void Void
e)

{-| Compile a Dhall expression to a Bash expression

    This only supports:

    * @Bool@s
    * @Natural@s
    * @Integer@s
    * @Text@s
 -}
dhallToExpression
    :: Expr s Void
    -- ^ Dhall expression to compile
    -> Either ExpressionError ByteString
    -- ^ Bash expression or compile failure
dhallToExpression :: Expr s Void -> Either ExpressionError ByteString
dhallToExpression Expr s Void
expr0 = Expr Void Void -> Either ExpressionError ByteString
go (Expr s Void -> Expr Void Void
forall a s t. Eq a => Expr s a -> Expr t a
Dhall.Core.normalize Expr s Void
expr0)
  where
    go :: Expr Void Void -> Either ExpressionError ByteString
go (BoolLit Bool
a) =
        Expr Void Void -> Either ExpressionError ByteString
go (Chunks Void Void -> Expr Void Void
forall s a. Chunks s a -> Expr s a
TextLit (if Bool
a then Chunks Void Void
"true" else Chunks Void Void
"false"))
    go (NaturalLit Natural
a) =
        Expr Void Void -> Either ExpressionError ByteString
go (Integer -> Expr Void Void
forall s a. Integer -> Expr s a
IntegerLit (Natural -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Natural
a))
    go (IntegerLit Integer
a) =
        Expr Void Void -> Either ExpressionError ByteString
go (Chunks Void Void -> Expr Void Void
forall s a. Chunks s a -> Expr s a
TextLit ([(Text, Expr Void Void)] -> Text -> Chunks Void Void
forall s a. [(Text, Expr s a)] -> Text -> Chunks s a
Chunks [] (String -> Text
Data.Text.pack (Integer -> String
forall a. Show a => a -> String
show Integer
a))))
    go (TextLit (Chunks [] Text
a)) = do
        let bytes :: ByteString
bytes = Text -> ByteString
Data.Text.Encoding.encodeUtf8 Text
a
        ByteString -> Either ExpressionError ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return (Bash -> ByteString
forall t. Escape t => t -> ByteString
Text.ShellEscape.bytes (ByteString -> Bash
Text.ShellEscape.bash ByteString
bytes))
    go e :: Expr Void Void
e@(Field (Union Map Text (Maybe (Expr Void Void))
m) (FieldSelection Void -> Text
forall s. FieldSelection s -> Text
Dhall.Core.fieldSelectionLabel -> Text
k)) =
        case Text
-> Map Text (Maybe (Expr Void Void))
-> Maybe (Maybe (Expr Void Void))
forall k v. Ord k => k -> Map k v -> Maybe v
Dhall.Map.lookup Text
k Map Text (Maybe (Expr Void Void))
m of
            Just Maybe (Expr Void Void)
Nothing -> Expr Void Void -> Either ExpressionError ByteString
go (Chunks Void Void -> Expr Void Void
forall s a. Chunks s a -> Expr s a
TextLit ([(Text, Expr Void Void)] -> Text -> Chunks Void Void
forall s a. [(Text, Expr s a)] -> Text -> Chunks s a
Chunks [] Text
k))
            Maybe (Maybe (Expr Void Void))
_            -> ExpressionError -> Either ExpressionError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> ExpressionError
UnsupportedExpression Expr Void Void
e)
    go Expr Void Void
e = ExpressionError -> Either ExpressionError ByteString
forall a b. a -> Either a b
Left (Expr Void Void -> ExpressionError
UnsupportedExpression Expr Void Void
e)