module Shikensu.Utilities
    ( (!~>)
    , (~>)
    , io
    , lsequence
    , mapIO
    ) where

import Data.Aeson (FromJSON, ToJSON, fromJSON)
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
import Data.Text (Text)
import Flow
import Shikensu.Internal.Types

import qualified Data.Aeson as Json (Object, Result(..), encode)
import qualified Data.Aeson.KeyMap as KeyMap (lookup)
import qualified Data.Aeson.Key as Key (fromText)
import qualified Data.List as List (unzip, zip)
import qualified Data.Text as Text (unpack)
import qualified Data.Text.Lazy as Lazy.Text (unpack)
import qualified Data.Text.Lazy.Encoding as Lazy.Text (decodeUtf8)
import qualified Data.Tuple as Tuple (fst, snd)


-- IO


{-| IO Sequence helpers
-}
io :: ([Definition] -> [IO Definition]) -> Dictionary -> IO Dictionary
io :: ([Definition] -> [IO Definition])
-> [Definition] -> IO [Definition]
io [Definition] -> [IO Definition]
fn =
    [Definition] -> [IO Definition]
fn ([Definition] -> [IO Definition])
-> ([IO Definition] -> IO [Definition])
-> [Definition]
-> IO [Definition]
forall a b c. (a -> b) -> (b -> c) -> a -> c
.> [IO Definition] -> IO [Definition]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence


mapIO :: (Definition -> IO Definition) -> Dictionary -> IO Dictionary
mapIO :: (Definition -> IO Definition) -> [Definition] -> IO [Definition]
mapIO =
    (Definition -> IO Definition) -> [Definition] -> [IO Definition]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Definition -> IO Definition) -> [Definition] -> [IO Definition])
-> (([Definition] -> [IO Definition])
    -> [Definition] -> IO [Definition])
-> (Definition -> IO Definition)
-> [Definition]
-> IO [Definition]
forall a b c. (a -> b) -> (b -> c) -> a -> c
.> ([Definition] -> [IO Definition])
-> [Definition] -> IO [Definition]
io


{-| One way to deal with multiple dictionaries.

> lsequence
>     [ ( "pages", Shikensu.list ["src/pages/**/*.html"] rootDir    )
>     , ( "js",    Shikensu.list ["src/javascript/**/*.js"] rootDir )
>     ]

From multiple IO monads to a single IO monad.
-}
lsequence :: Monad m => [( id, m a )] -> m [( id, a )]
lsequence :: [(id, m a)] -> m [(id, a)]
lsequence [(id, m a)]
list =
    let
        unzippedList :: ([id], [m a])
unzippedList =
            [(id, m a)] -> ([id], [m a])
forall a b. [(a, b)] -> ([a], [b])
List.unzip [(id, m a)]
list

        identifiers :: [id]
identifiers =
            ([id], [m a]) -> [id]
forall a b. (a, b) -> a
Tuple.fst ([id], [m a])
unzippedList

        dictionaries :: [m a]
dictionaries =
            ([id], [m a]) -> [m a]
forall a b. (a, b) -> b
Tuple.snd ([id], [m a])
unzippedList
    in
        [m a]
dictionaries
            [m a] -> ([m a] -> m [a]) -> m [a]
forall a b. a -> (a -> b) -> b
|> [m a] -> m [a]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence
            m [a] -> (m [a] -> m [(id, a)]) -> m [(id, a)]
forall a b. a -> (a -> b) -> b
|> ([a] -> [(id, a)]) -> m [a] -> m [(id, a)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([id] -> [a] -> [(id, a)]
forall a b. [a] -> [b] -> [(a, b)]
List.zip [id]
identifiers)



-- PURE


{-| Get stuff out of the metadata.
    Returns a `Maybe`.
-}
(~>) :: (FromJSON a, ToJSON a) => Metadata -> Text -> Maybe a
~> :: Metadata -> Text -> Maybe a
(~>) Metadata
obj Text
key =
    Metadata
obj
        Metadata -> (Metadata -> Maybe Value) -> Maybe Value
forall a b. a -> (a -> b) -> b
|> Key -> Metadata -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KeyMap.lookup (Text -> Key
Key.fromText Text
key)
        Maybe Value
-> (Maybe Value -> Maybe (Result a)) -> Maybe (Result a)
forall a b. a -> (a -> b) -> b
|> (Value -> Result a) -> Maybe Value -> Maybe (Result a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Value -> Result a
forall a. FromJSON a => Value -> Result a
fromJSON
        Maybe (Result a) -> (Maybe (Result a) -> Maybe a) -> Maybe a
forall a b. a -> (a -> b) -> b
|> (Result a -> a) -> Maybe (Result a) -> Maybe a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Result a -> a
forall a. ToJSON a => Result a -> a
fromJSONResult


{-| Get stuff out of the metadata.
    Does NOT return a `Maybe`, but gives an error if it isn't found.
-}
(!~>) :: (FromJSON a, ToJSON a) => Metadata -> Text -> a
!~> :: Metadata -> Text -> a
(!~>) Metadata
obj Text
key =
    case (Metadata
obj Metadata -> Text -> Maybe a
forall a. (FromJSON a, ToJSON a) => Metadata -> Text -> Maybe a
~> Text
key) of
        Just a
x  -> a
x
        Maybe a
Nothing -> [Char] -> a
forall a. HasCallStack => [Char] -> a
error ([Char] -> a) -> [Char] -> a
forall a b. (a -> b) -> a -> b
<|
            [Char]
"Could not find the key `" [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> Text -> [Char]
Text.unpack Text
key         [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"` " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>
            [Char]
"on the metadata object `" [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> Metadata -> [Char]
aesonObjectToString Metadata
obj [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"` using (!~>)"



-- Private


fromJSONResult :: ToJSON a => Json.Result a -> a
fromJSONResult :: Result a -> a
fromJSONResult Result a
result =
    case Result a
result of
        Json.Success a
x -> a
x
        Json.Error [Char]
err -> [Char] -> a
forall a. HasCallStack => [Char] -> a
error [Char]
err


aesonObjectToString :: Json.Object -> String
aesonObjectToString :: Metadata -> [Char]
aesonObjectToString =
    Metadata -> ByteString
forall a. ToJSON a => a -> ByteString
Json.encode (Metadata -> ByteString)
-> (ByteString -> Text) -> Metadata -> Text
forall a b c. (a -> b) -> (b -> c) -> a -> c
.> ByteString -> Text
Lazy.Text.decodeUtf8 (Metadata -> Text) -> (Text -> [Char]) -> Metadata -> [Char]
forall a b c. (a -> b) -> (b -> c) -> a -> c
.> Text -> [Char]
Lazy.Text.unpack