-- | Content defined in text files (.b9 files), read with the 'Read' instances.
module B9.Artifact.Content.Readable where

import B9.Artifact.Content
import B9.Artifact.Content.AST
import B9.Artifact.Content.CloudConfigYaml
import B9.Artifact.Content.ErlangPropList
import B9.Artifact.Content.StringTemplate
import B9.Artifact.Content.YamlObject
import B9.B9Logging
import B9.QCUtil
import B9.Text
import Control.Monad.IO.Class
import Control.Parallel.Strategies
import qualified Data.ByteString as Strict
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.Lazy as Lazy
import Data.Data
import GHC.Generics (Generic)
import GHC.Stack
import System.Exit
import System.Process
import Test.QuickCheck

-- | This is content that can be 'read' via the generated 'Read' instance.
data Content
  = RenderErlang (AST Content ErlangPropList)
  | RenderYamlObject (AST Content YamlObject)
  | RenderCloudConfig (AST Content CloudConfigYaml)
  | -- | This data will be passed through unaltered.
    -- This is used during the transition phase from having B9 stuff read from
    -- files via 'Read' instances towards programatic use or the use of HOCON.
    --
    -- @since 0.5.62
    FromByteString Lazy.ByteString
  | -- | Embed a literal string
    FromString String
  | -- | Embed the contents of the 'SourceFile' with template parameter substitution.
    FromTextFile SourceFile
  | -- | The data in the given file will be base64 encoded.
    RenderBase64BinaryFile FilePath
  | -- | This data will be base64 encoded.
    RenderBase64Binary Lazy.ByteString
  | -- | Download the contents of the URL
    FromURL String
  deriving (ReadPrec [Content]
ReadPrec Content
Int -> ReadS Content
ReadS [Content]
(Int -> ReadS Content)
-> ReadS [Content]
-> ReadPrec Content
-> ReadPrec [Content]
-> Read Content
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Content]
$creadListPrec :: ReadPrec [Content]
readPrec :: ReadPrec Content
$creadPrec :: ReadPrec Content
readList :: ReadS [Content]
$creadList :: ReadS [Content]
readsPrec :: Int -> ReadS Content
$creadsPrec :: Int -> ReadS Content
Read, Int -> Content -> ShowS
[Content] -> ShowS
Content -> String
(Int -> Content -> ShowS)
-> (Content -> String) -> ([Content] -> ShowS) -> Show Content
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Content] -> ShowS
$cshowList :: [Content] -> ShowS
show :: Content -> String
$cshow :: Content -> String
showsPrec :: Int -> Content -> ShowS
$cshowsPrec :: Int -> Content -> ShowS
Show, Typeable, Content -> Content -> Bool
(Content -> Content -> Bool)
-> (Content -> Content -> Bool) -> Eq Content
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Content -> Content -> Bool
$c/= :: Content -> Content -> Bool
== :: Content -> Content -> Bool
$c== :: Content -> Content -> Bool
Eq, Typeable Content
DataType
Constr
Typeable Content
-> (forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> Content -> c Content)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Content)
-> (Content -> Constr)
-> (Content -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c Content))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Content))
-> ((forall b. Data b => b -> b) -> Content -> Content)
-> (forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> Content -> r)
-> (forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> Content -> r)
-> (forall u. (forall d. Data d => d -> u) -> Content -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> Content -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> Content -> m Content)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Content -> m Content)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Content -> m Content)
-> Data Content
Content -> DataType
Content -> Constr
(forall b. Data b => b -> b) -> Content -> Content
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Content -> c Content
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Content
forall a.
Typeable a
-> (forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> Content -> u
forall u. (forall d. Data d => d -> u) -> Content -> [u]
forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> Content -> r
forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> Content -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Content -> m Content
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Content -> m Content
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Content
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Content -> c Content
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Content)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Content)
$cFromURL :: Constr
$cRenderBase64Binary :: Constr
$cRenderBase64BinaryFile :: Constr
$cFromTextFile :: Constr
$cFromString :: Constr
$cFromByteString :: Constr
$cRenderCloudConfig :: Constr
$cRenderYamlObject :: Constr
$cRenderErlang :: Constr
$tContent :: DataType
gmapMo :: (forall d. Data d => d -> m d) -> Content -> m Content
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Content -> m Content
gmapMp :: (forall d. Data d => d -> m d) -> Content -> m Content
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Content -> m Content
gmapM :: (forall d. Data d => d -> m d) -> Content -> m Content
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Content -> m Content
gmapQi :: Int -> (forall d. Data d => d -> u) -> Content -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Content -> u
gmapQ :: (forall d. Data d => d -> u) -> Content -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> Content -> [u]
gmapQr :: (r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> Content -> r
$cgmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> Content -> r
gmapQl :: (r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> Content -> r
$cgmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> Content -> r
gmapT :: (forall b. Data b => b -> b) -> Content -> Content
$cgmapT :: (forall b. Data b => b -> b) -> Content -> Content
dataCast2 :: (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Content)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Content)
dataCast1 :: (forall d. Data d => c (t d)) -> Maybe (c Content)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Content)
dataTypeOf :: Content -> DataType
$cdataTypeOf :: Content -> DataType
toConstr :: Content -> Constr
$ctoConstr :: Content -> Constr
gunfold :: (forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Content
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Content
gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Content -> c Content
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Content -> c Content
$cp1Data :: Typeable Content
Data, (forall x. Content -> Rep Content x)
-> (forall x. Rep Content x -> Content) -> Generic Content
forall x. Rep Content x -> Content
forall x. Content -> Rep Content x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Content x -> Content
$cfrom :: forall x. Content -> Rep Content x
Generic)

instance NFData Content

instance Arbitrary Content where
  arbitrary :: Gen Content
arbitrary =
    [Gen Content] -> Gen Content
forall a. [Gen a] -> Gen a
oneof
      [ SourceFile -> Content
FromTextFile (SourceFile -> Content) -> Gen SourceFile -> Gen Content
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen SourceFile -> Gen SourceFile
forall a. Gen a -> Gen a
smaller Gen SourceFile
forall a. Arbitrary a => Gen a
arbitrary,
        String -> Content
RenderBase64BinaryFile (String -> Content) -> Gen String -> Gen Content
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen String -> Gen String
forall a. Gen a -> Gen a
smaller Gen String
forall a. Arbitrary a => Gen a
arbitrary,
        AST Content ErlangPropList -> Content
RenderErlang (AST Content ErlangPropList -> Content)
-> Gen (AST Content ErlangPropList) -> Gen Content
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen (AST Content ErlangPropList)
-> Gen (AST Content ErlangPropList)
forall a. Gen a -> Gen a
smaller Gen (AST Content ErlangPropList)
forall a. Arbitrary a => Gen a
arbitrary,
        AST Content YamlObject -> Content
RenderYamlObject (AST Content YamlObject -> Content)
-> Gen (AST Content YamlObject) -> Gen Content
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen (AST Content YamlObject) -> Gen (AST Content YamlObject)
forall a. Gen a -> Gen a
smaller Gen (AST Content YamlObject)
forall a. Arbitrary a => Gen a
arbitrary,
        AST Content CloudConfigYaml -> Content
RenderCloudConfig (AST Content CloudConfigYaml -> Content)
-> Gen (AST Content CloudConfigYaml) -> Gen Content
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen (AST Content CloudConfigYaml)
-> Gen (AST Content CloudConfigYaml)
forall a. Gen a -> Gen a
smaller Gen (AST Content CloudConfigYaml)
forall a. Arbitrary a => Gen a
arbitrary,
        String -> Content
FromString (String -> Content) -> Gen String -> Gen Content
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen String -> Gen String
forall a. Gen a -> Gen a
smaller Gen String
forall a. Arbitrary a => Gen a
arbitrary,
        ByteString -> Content
FromByteString (ByteString -> Content)
-> ([Word8] -> ByteString) -> [Word8] -> Content
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Word8] -> ByteString
Lazy.pack ([Word8] -> Content) -> Gen [Word8] -> Gen Content
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen [Word8] -> Gen [Word8]
forall a. Gen a -> Gen a
smaller Gen [Word8]
forall a. Arbitrary a => Gen a
arbitrary,
        ByteString -> Content
RenderBase64Binary (ByteString -> Content)
-> ([Word8] -> ByteString) -> [Word8] -> Content
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Word8] -> ByteString
Lazy.pack ([Word8] -> Content) -> Gen [Word8] -> Gen Content
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen [Word8] -> Gen [Word8]
forall a. Gen a -> Gen a
smaller Gen [Word8]
forall a. Arbitrary a => Gen a
arbitrary,
        String -> Content
FromURL (String -> Content) -> Gen String -> Gen Content
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen String -> Gen String
forall a. Gen a -> Gen a
smaller Gen String
forall a. Arbitrary a => Gen a
arbitrary
      ]

instance ToContentGenerator Content where
  toContentGenerator :: Content -> Eff e Text
toContentGenerator (RenderErlang AST Content ErlangPropList
ast) = ErlangPropList -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText (ErlangPropList -> Text) -> Eff e ErlangPropList -> Eff e Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> AST Content ErlangPropList -> Eff e ErlangPropList
forall a (e :: [* -> *]) c.
(FromAST a, IsB9 e, ToContentGenerator c) =>
AST c a -> Eff e a
fromAST AST Content ErlangPropList
ast
  toContentGenerator (RenderYamlObject AST Content YamlObject
ast) =
    YamlObject -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText (YamlObject -> Text) -> Eff e YamlObject -> Eff e Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> AST Content YamlObject -> Eff e YamlObject
forall a (e :: [* -> *]) c.
(FromAST a, IsB9 e, ToContentGenerator c) =>
AST c a -> Eff e a
fromAST AST Content YamlObject
ast
  toContentGenerator (RenderCloudConfig AST Content CloudConfigYaml
ast) =
    CloudConfigYaml -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText (CloudConfigYaml -> Text) -> Eff e CloudConfigYaml -> Eff e Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> AST Content CloudConfigYaml -> Eff e CloudConfigYaml
forall a (e :: [* -> *]) c.
(FromAST a, IsB9 e, ToContentGenerator c) =>
AST c a -> Eff e a
fromAST AST Content CloudConfigYaml
ast
  toContentGenerator (FromTextFile SourceFile
s) = SourceFile -> Eff e Text
forall (e :: [* -> *]).
(MonadIO (Eff e), '[ExcB9, EnvironmentReader] <:: e) =>
SourceFile -> Eff e Text
readTemplateFile SourceFile
s
  toContentGenerator (RenderBase64BinaryFile String
s) = String -> Eff e Text
forall (m :: * -> *). (HasCallStack, MonadIO m) => String -> m Text
readBinaryFileAsBase64 String
s
    where
      readBinaryFileAsBase64 :: (HasCallStack, MonadIO m) => FilePath -> m Text
      readBinaryFileAsBase64 :: String -> m Text
readBinaryFileAsBase64 String
f =
        ByteString -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText (ByteString -> Text)
-> (ByteString -> ByteString) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
B64.encode (ByteString -> Text) -> m ByteString -> m Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO ByteString -> m ByteString
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO ByteString
Strict.readFile String
f)
  toContentGenerator (RenderBase64Binary ByteString
b) =
    Text -> Eff e Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText (ByteString -> Text)
-> (ByteString -> ByteString) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
B64.encode (ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
Lazy.toStrict (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString
b)
  toContentGenerator (FromString String
str) = Text -> Eff e Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure (String -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText String
str)
  toContentGenerator (FromByteString ByteString
str) =
    Text -> Eff e Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText (ByteString -> Text)
-> (ByteString -> ByteString) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
Lazy.toStrict (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString
str)
  toContentGenerator (FromURL String
url) = do
    String -> Eff e ()
forall (e :: [* -> *]). CommandIO e => String -> Eff e ()
dbgL (String
"Downloading: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
url)
    (ExitCode
exitCode, String
out, String
err) <- IO (ExitCode, String, String) -> Eff e (ExitCode, String, String)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> [String] -> String -> IO (ExitCode, String, String)
readProcessWithExitCode String
"curl" [String
url] String
"")
    if ExitCode
exitCode ExitCode -> ExitCode -> Bool
forall a. Eq a => a -> a -> Bool
== ExitCode
ExitSuccess
      then do
        String -> Eff e ()
forall (e :: [* -> *]). CommandIO e => String -> Eff e ()
dbgL (String
"Download finished. Bytes read: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
out))
        String -> Eff e ()
forall (e :: [* -> *]). CommandIO e => String -> Eff e ()
traceL
          ( String
"Downloaded (truncated to first 4K): \n\n" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> ShowS
forall a. Int -> [a] -> [a]
take Int
4096 String
out String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\n\n"
          )
        Text -> Eff e Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure (String -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText String
out)
      else do
        String -> Eff e ()
forall (e :: [* -> *]). CommandIO e => String -> Eff e ()
errorL (String
"Download failed: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
err)
        IO Text -> Eff e Text
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (ExitCode -> IO Text
forall a. ExitCode -> IO a
exitWith ExitCode
exitCode)

-- ** Convenient Aliases

-- | An 'ErlangPropList' 'AST' with 'Content'
--
-- @since 0.5.67
type ErlangAst = AST Content ErlangPropList