{-#LANGUAGE OverloadedStrings #-}
{-#LANGUAGE MultiParamTypeClasses #-}
{-#LANGUAGE FlexibleInstances #-}
{-#LANGUAGE ScopedTypeVariables #-}
{-#LANGUAGE RankNTypes #-}

-- | GVal is a generic unitype value, representing the kind of values that
-- Ginger can understand.
--
-- Most of the types in this module are parametrized over an 'm' type, which
-- is the host monad for template execution, as passed to 'runGingerT'. For
-- most kinds of values, 'm' is transparent, and in many cases a 'ToGVal'
-- instance can be written that works for all possible 'm'; the reason we need
-- to parametrize the values themselves over the carrier monad is because we
-- want to support impure functions, which requires access to the underlying
-- carrier monad (e.g. 'IO').
module Text.Ginger.GVal
where

import Prelude ( (.), ($), (==), (/=)
               , (++), (+), (-), (*), (/), div
               , (=<<), (>>=), return
               , (||), (&&)
               , undefined, otherwise, id, const
               , fmap
               , Maybe (..)
               , Bool (..)
               , Either (..)
               , Char
               , Int
               , Integer
               , Double
               , Show, show
               , Integral
               , fromIntegral, floor
               , not
               , fst, snd
               , Monad
               , Functor
               )
import Control.Monad.Fail (MonadFail)
import qualified Prelude
import Data.Maybe ( fromMaybe, catMaybes, isJust, mapMaybe )
import Data.Text (Text)
import Data.String (IsString, fromString)
import qualified Data.Text as Text
import qualified Data.Text.Lazy as LText
import qualified Data.List as List
import Safe (readMay, atMay)
import Data.Monoid
import Data.Scientific ( Scientific
                       , floatingOrInteger
                       , toBoundedInteger
                       , toRealFloat
                       , scientific
                       , coefficient
                       , base10Exponent
                       )
import Data.Fixed (Fixed (..), Pico)
import Control.Applicative
import qualified Data.Aeson as JSON
import qualified Data.HashMap.Strict as HashMap
import Data.HashMap.Strict (HashMap)
import qualified Data.Map.Strict as Map
import Data.Map.Strict (Map)
import qualified Data.Vector as Vector
import Control.Monad ((<=<), forM, mapM)
import Control.Monad.Trans (MonadTrans, lift)
import Data.Default (Default, def)
import Text.Printf
import Debug.Trace (trace)
import Data.Time ( Day (..)
                 , defaultTimeLocale
                 , toModifiedJulianDay
                 , formatTime
                 , toGregorian
                 , fromGregorian
                 , LocalTime (..)
                 , ZonedTime (..)
                 , TimeOfDay (..)
                 , TimeZone (..)
                 , TimeLocale (..)
                 )
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Data.Text.Encoding (encodeUtf8, decodeUtf8)
import qualified Data.Text.Lazy.Encoding as LText

import Text.Ginger.Html

-- * The Ginger Value type
--
-- | A variant type designed as the unitype for the template language. Any
-- value referenced in a template, returned from within a template, or used
-- in a template context, will be a 'GVal'.
-- @m@, in most cases, should be a 'Monad'.
--
-- Some laws apply here, most notably:
--
-- - when 'isNull' is 'True', then all of 'asFunction', 'asText', 'asNumber',
--   'asHtml', 'asList', 'asDictItems', and 'length' should produce 'Nothing'
-- - when 'isNull' is 'True', then 'asBoolean' should produce 'False'
-- - when 'asNumber' is not 'Nothing', then 'asBoolean' should only return
--   'False' for exactly zero
-- - 'Nothing'-ness of 'length' should match one or both of 'asList' / 'asDictItems'
data GVal m =
    GVal
        { GVal m -> Maybe [GVal m]
asList :: Maybe [GVal m] -- ^ Convert value to list, if possible
        , GVal m -> Maybe [(Text, GVal m)]
asDictItems :: Maybe [(Text, GVal m)] -- ^ Convert value to association list ("dictionary"), if possible
        , GVal m -> Maybe (Text -> Maybe (GVal m))
asLookup :: Maybe (Text -> Maybe (GVal m)) -- ^ Convert value to a lookup function
        , GVal m -> Html
asHtml :: Html -- ^ Render value as HTML
        , GVal m -> Text
asText :: Text -- ^ Render value as plain-text
        , GVal m -> Bool
asBoolean :: Bool -- ^ Get value's truthiness
        , GVal m -> Maybe Scientific
asNumber :: Maybe Scientific -- ^ Convert value to a number, if possible
        , GVal m -> Maybe (Function m)
asFunction :: Maybe (Function m) -- ^ Access value as a callable function, if it is one
        , GVal m -> Maybe ByteString
asBytes :: Maybe ByteString -- ^ Access as raw bytes
        , GVal m -> Maybe Int
length :: Maybe Int -- ^ Get length of value, if it is a collection (list/dict)
        , GVal m -> Bool
isNull :: Bool -- ^ Check if the value is null
        , GVal m -> Maybe Value
asJSON :: Maybe JSON.Value -- ^ Provide a custom JSON representation of the value
        }

gappend :: GVal m -> GVal m -> GVal m
gappend :: GVal m -> GVal m -> GVal m
gappend GVal m
a GVal m
b =
  GVal :: forall (m :: * -> *).
Maybe [GVal m]
-> Maybe [(Text, GVal m)]
-> Maybe (Text -> Maybe (GVal m))
-> Html
-> Text
-> Bool
-> Maybe Scientific
-> Maybe (Function m)
-> Maybe ByteString
-> Maybe Int
-> Bool
-> Maybe Value
-> GVal m
GVal
    { asList :: Maybe [GVal m]
asList = [GVal m] -> [GVal m] -> [GVal m]
forall a. [a] -> [a] -> [a]
(++) ([GVal m] -> [GVal m] -> [GVal m])
-> Maybe [GVal m] -> Maybe ([GVal m] -> [GVal m])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
a Maybe ([GVal m] -> [GVal m]) -> Maybe [GVal m] -> Maybe [GVal m]
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
b
    , asDictItems :: Maybe [(Text, GVal m)]
asDictItems = [(Text, GVal m)] -> [(Text, GVal m)] -> [(Text, GVal m)]
forall a. [a] -> [a] -> [a]
(++) ([(Text, GVal m)] -> [(Text, GVal m)] -> [(Text, GVal m)])
-> Maybe [(Text, GVal m)]
-> Maybe ([(Text, GVal m)] -> [(Text, GVal m)])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe [(Text, GVal m)]
forall (m :: * -> *). GVal m -> Maybe [(Text, GVal m)]
asDictItems GVal m
a Maybe ([(Text, GVal m)] -> [(Text, GVal m)])
-> Maybe [(Text, GVal m)] -> Maybe [(Text, GVal m)]
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GVal m -> Maybe [(Text, GVal m)]
forall (m :: * -> *). GVal m -> Maybe [(Text, GVal m)]
asDictItems GVal m
b
    , asLookup :: Maybe (Text -> Maybe (GVal m))
asLookup = do
        Text -> Maybe (GVal m)
lookupA <- GVal m -> Maybe (Text -> Maybe (GVal m))
forall (m :: * -> *). GVal m -> Maybe (Text -> Maybe (GVal m))
asLookup GVal m
a
        Text -> Maybe (GVal m)
lookupB <- GVal m -> Maybe (Text -> Maybe (GVal m))
forall (m :: * -> *). GVal m -> Maybe (Text -> Maybe (GVal m))
asLookup GVal m
b
        (Text -> Maybe (GVal m)) -> Maybe (Text -> Maybe (GVal m))
forall (m :: * -> *) a. Monad m => a -> m a
return ((Text -> Maybe (GVal m)) -> Maybe (Text -> Maybe (GVal m)))
-> (Text -> Maybe (GVal m)) -> Maybe (Text -> Maybe (GVal m))
forall a b. (a -> b) -> a -> b
$ \Text
k -> Text -> Maybe (GVal m)
lookupA Text
k Maybe (GVal m) -> Maybe (GVal m) -> Maybe (GVal m)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Text -> Maybe (GVal m)
lookupB Text
k
    , asHtml :: Html
asHtml = GVal m -> Html
forall (m :: * -> *). GVal m -> Html
asHtml GVal m
a Html -> Html -> Html
forall a. Semigroup a => a -> a -> a
<> GVal m -> Html
forall (m :: * -> *). GVal m -> Html
asHtml GVal m
b
    , asText :: Text
asText = GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
a Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
b
    , asBytes :: Maybe ByteString
asBytes = GVal m -> Maybe ByteString
forall (m :: * -> *). GVal m -> Maybe ByteString
asBytes GVal m
a Maybe ByteString -> Maybe ByteString -> Maybe ByteString
forall a. Semigroup a => a -> a -> a
<> GVal m -> Maybe ByteString
forall (m :: * -> *). GVal m -> Maybe ByteString
asBytes GVal m
b
    , asBoolean :: Bool
asBoolean = (GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
asBoolean GVal m
a Bool -> Bool -> Bool
|| GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
asBoolean GVal m
b) Bool -> Bool -> Bool
&& Bool -> Bool
not (GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isNull GVal m
a Bool -> Bool -> Bool
|| GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isNull GVal m
b)
    , asNumber :: Maybe Scientific
asNumber = String -> Maybe Scientific
forall a. Read a => String -> Maybe a
readMay (String -> Maybe Scientific)
-> (Text -> String) -> Text -> Maybe Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
Text.unpack (Text -> Maybe Scientific) -> Text -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ (GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
a Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
b)
    , asFunction :: Maybe (Function m)
asFunction = Maybe (Function m)
forall a. Maybe a
Nothing
    , isNull :: Bool
isNull = GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isNull GVal m
a Bool -> Bool -> Bool
|| GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isNull GVal m
b
    , asJSON :: Maybe Value
asJSON = case (GVal m -> Value
forall a. ToJSON a => a -> Value
JSON.toJSON GVal m
a, GVal m -> Value
forall a. ToJSON a => a -> Value
JSON.toJSON GVal m
b) of
        (JSON.Array Array
x, JSON.Array Array
y) -> Value -> Maybe Value
forall a. a -> Maybe a
Just (Value -> Maybe Value) -> Value -> Maybe Value
forall a b. (a -> b) -> a -> b
$ Array -> Value
JSON.Array (Array
x Array -> Array -> Array
forall a. Semigroup a => a -> a -> a
<> Array
y)
        (JSON.Object Object
x, JSON.Object Object
y) -> Value -> Maybe Value
forall a. a -> Maybe a
Just (Value -> Maybe Value) -> Value -> Maybe Value
forall a b. (a -> b) -> a -> b
$ Object -> Value
JSON.Object (Object
x Object -> Object -> Object
forall a. Semigroup a => a -> a -> a
<> Object
y)
        (JSON.String Text
x, JSON.String Text
y) -> Value -> Maybe Value
forall a. a -> Maybe a
Just (Value -> Maybe Value) -> Value -> Maybe Value
forall a b. (a -> b) -> a -> b
$ Text -> Value
JSON.String (Text
x Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
y)
        (Value
JSON.Null, Value
b) -> Value -> Maybe Value
forall a. a -> Maybe a
Just (Value -> Maybe Value) -> Value -> Maybe Value
forall a b. (a -> b) -> a -> b
$ Value
b
        (Value
a, Value
JSON.Null) -> Value -> Maybe Value
forall a. a -> Maybe a
Just (Value -> Maybe Value) -> Value -> Maybe Value
forall a b. (a -> b) -> a -> b
$ Value
a
        (Value, Value)
_ -> Maybe Value
forall a. Maybe a
Nothing -- If JSON tags mismatch, use default toJSON impl
    , length :: Maybe Int
length = Int -> Int -> Int
forall a. Num a => a -> a -> a
(+) (Int -> Int -> Int) -> Maybe Int -> Maybe (Int -> Int)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe Int
forall (m :: * -> *). GVal m -> Maybe Int
length GVal m
a Maybe (Int -> Int) -> Maybe Int -> Maybe Int
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GVal m -> Maybe Int
forall (m :: * -> *). GVal m -> Maybe Int
length GVal m
b
    }

-- | Marshal a GVal between carrier monads.
-- This will lose 'asFunction' information, because functions cannot be
-- transferred to other carrier monads, but it will keep all other data
-- structures intact.
marshalGVal :: GVal m -> GVal n
marshalGVal :: GVal m -> GVal n
marshalGVal GVal m
g =
    GVal :: forall (m :: * -> *).
Maybe [GVal m]
-> Maybe [(Text, GVal m)]
-> Maybe (Text -> Maybe (GVal m))
-> Html
-> Text
-> Bool
-> Maybe Scientific
-> Maybe (Function m)
-> Maybe ByteString
-> Maybe Int
-> Bool
-> Maybe Value
-> GVal m
GVal
        { asList :: Maybe [GVal n]
asList = (GVal m -> GVal n) -> [GVal m] -> [GVal n]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap GVal m -> GVal n
forall (m :: * -> *) (n :: * -> *). GVal m -> GVal n
marshalGVal ([GVal m] -> [GVal n]) -> Maybe [GVal m] -> Maybe [GVal n]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
g
        , asDictItems :: Maybe [(Text, GVal n)]
asDictItems = ([(Text, GVal m)] -> [(Text, GVal n)])
-> Maybe [(Text, GVal m)] -> Maybe [(Text, GVal n)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\[(Text, GVal m)]
items -> [(Text
k, GVal m -> GVal n
forall (m :: * -> *) (n :: * -> *). GVal m -> GVal n
marshalGVal GVal m
v) | (Text
k, GVal m
v) <- [(Text, GVal m)]
items]) (GVal m -> Maybe [(Text, GVal m)]
forall (m :: * -> *). GVal m -> Maybe [(Text, GVal m)]
asDictItems GVal m
g)
        , asLookup :: Maybe (Text -> Maybe (GVal n))
asLookup = ((Text -> Maybe (GVal m)) -> Text -> Maybe (GVal n))
-> Maybe (Text -> Maybe (GVal m)) -> Maybe (Text -> Maybe (GVal n))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((GVal m -> GVal n) -> Maybe (GVal m) -> Maybe (GVal n)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap GVal m -> GVal n
forall (m :: * -> *) (n :: * -> *). GVal m -> GVal n
marshalGVal (Maybe (GVal m) -> Maybe (GVal n))
-> (Text -> Maybe (GVal m)) -> Text -> Maybe (GVal n)
forall b c a. (b -> c) -> (a -> b) -> a -> c
.) (GVal m -> Maybe (Text -> Maybe (GVal m))
forall (m :: * -> *). GVal m -> Maybe (Text -> Maybe (GVal m))
asLookup GVal m
g)
        , asHtml :: Html
asHtml = GVal m -> Html
forall (m :: * -> *). GVal m -> Html
asHtml GVal m
g
        , asText :: Text
asText = GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
g
        , asBytes :: Maybe ByteString
asBytes = GVal m -> Maybe ByteString
forall (m :: * -> *). GVal m -> Maybe ByteString
asBytes GVal m
g
        , asBoolean :: Bool
asBoolean = GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
asBoolean GVal m
g
        , asNumber :: Maybe Scientific
asNumber = GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber GVal m
g
        , asFunction :: Maybe (Function n)
asFunction = Maybe (Function n)
forall a. Maybe a
Nothing
        , isNull :: Bool
isNull = GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isNull GVal m
g
        , length :: Maybe Int
length = GVal m -> Maybe Int
forall (m :: * -> *). GVal m -> Maybe Int
length GVal m
g
        , asJSON :: Maybe Value
asJSON = GVal m -> Maybe Value
forall (m :: * -> *). GVal m -> Maybe Value
asJSON GVal m
g
        }

-- | Marshal a GVal between carrier monads.
-- Unlike 'marshalGVal', 'asFunction' information is retained by hoisting
-- them using the provided hoisting functions. For 'Run' monads, which is
-- what 'GVal' is typically used with, the 'hoistRun' function can be used
-- to construct suitable hoisting functions.
marshalGValEx :: (Functor m, Functor n)
              => (forall a. m a -> n a)
              -> (forall a. n a -> m a)
              -> GVal m
              -> GVal n
marshalGValEx :: (forall a. m a -> n a)
-> (forall a. n a -> m a) -> GVal m -> GVal n
marshalGValEx forall a. m a -> n a
hoist forall a. n a -> m a
unhoist GVal m
g =
    GVal :: forall (m :: * -> *).
Maybe [GVal m]
-> Maybe [(Text, GVal m)]
-> Maybe (Text -> Maybe (GVal m))
-> Html
-> Text
-> Bool
-> Maybe Scientific
-> Maybe (Function m)
-> Maybe ByteString
-> Maybe Int
-> Bool
-> Maybe Value
-> GVal m
GVal
        { asList :: Maybe [GVal n]
asList = (GVal m -> GVal n) -> [GVal m] -> [GVal n]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((forall a. m a -> n a)
-> (forall a. n a -> m a) -> GVal m -> GVal n
forall (m :: * -> *) (n :: * -> *).
(Functor m, Functor n) =>
(forall a. m a -> n a)
-> (forall a. n a -> m a) -> GVal m -> GVal n
marshalGValEx forall a. m a -> n a
hoist forall a. n a -> m a
unhoist) ([GVal m] -> [GVal n]) -> Maybe [GVal m] -> Maybe [GVal n]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
g
        , asDictItems :: Maybe [(Text, GVal n)]
asDictItems = ([(Text, GVal m)] -> [(Text, GVal n)])
-> Maybe [(Text, GVal m)] -> Maybe [(Text, GVal n)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\[(Text, GVal m)]
items -> [(Text
k, (forall a. m a -> n a)
-> (forall a. n a -> m a) -> GVal m -> GVal n
forall (m :: * -> *) (n :: * -> *).
(Functor m, Functor n) =>
(forall a. m a -> n a)
-> (forall a. n a -> m a) -> GVal m -> GVal n
marshalGValEx forall a. m a -> n a
hoist forall a. n a -> m a
unhoist GVal m
v) | (Text
k, GVal m
v) <- [(Text, GVal m)]
items]) (GVal m -> Maybe [(Text, GVal m)]
forall (m :: * -> *). GVal m -> Maybe [(Text, GVal m)]
asDictItems GVal m
g)
        , asLookup :: Maybe (Text -> Maybe (GVal n))
asLookup = ((Text -> Maybe (GVal m)) -> Text -> Maybe (GVal n))
-> Maybe (Text -> Maybe (GVal m)) -> Maybe (Text -> Maybe (GVal n))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((GVal m -> GVal n) -> Maybe (GVal m) -> Maybe (GVal n)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((forall a. m a -> n a)
-> (forall a. n a -> m a) -> GVal m -> GVal n
forall (m :: * -> *) (n :: * -> *).
(Functor m, Functor n) =>
(forall a. m a -> n a)
-> (forall a. n a -> m a) -> GVal m -> GVal n
marshalGValEx forall a. m a -> n a
hoist forall a. n a -> m a
unhoist) (Maybe (GVal m) -> Maybe (GVal n))
-> (Text -> Maybe (GVal m)) -> Text -> Maybe (GVal n)
forall b c a. (b -> c) -> (a -> b) -> a -> c
.) (GVal m -> Maybe (Text -> Maybe (GVal m))
forall (m :: * -> *). GVal m -> Maybe (Text -> Maybe (GVal m))
asLookup GVal m
g)
        , asHtml :: Html
asHtml = GVal m -> Html
forall (m :: * -> *). GVal m -> Html
asHtml GVal m
g
        , asText :: Text
asText = GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
g
        , asBytes :: Maybe ByteString
asBytes = GVal m -> Maybe ByteString
forall (m :: * -> *). GVal m -> Maybe ByteString
asBytes GVal m
g
        , asBoolean :: Bool
asBoolean = GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
asBoolean GVal m
g
        , asNumber :: Maybe Scientific
asNumber = GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber GVal m
g
        , asFunction :: Maybe (Function n)
asFunction = (forall a. m a -> n a)
-> (forall a. n a -> m a) -> Function m -> Function n
forall (m :: * -> *) (n :: * -> *).
(Functor m, Functor n) =>
(forall a. m a -> n a)
-> (forall a. n a -> m a) -> Function m -> Function n
marshalFunction forall a. m a -> n a
hoist forall a. n a -> m a
unhoist (Function m -> Function n)
-> Maybe (Function m) -> Maybe (Function n)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe (Function m)
forall (m :: * -> *). GVal m -> Maybe (Function m)
asFunction GVal m
g
        , isNull :: Bool
isNull = GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isNull GVal m
g
        , length :: Maybe Int
length = GVal m -> Maybe Int
forall (m :: * -> *). GVal m -> Maybe Int
length GVal m
g
        , asJSON :: Maybe Value
asJSON = GVal m -> Maybe Value
forall (m :: * -> *). GVal m -> Maybe Value
asJSON GVal m
g
        }

marshalFunction :: (Functor m, Functor n) => (forall a. m a -> n a) -> (forall a. n a -> m a) -> Function m -> Function n
-- [(Maybe Text, GVal m)] -> m (GVal m)
marshalFunction :: (forall a. m a -> n a)
-> (forall a. n a -> m a) -> Function m -> Function n
marshalFunction forall a. m a -> n a
hoist forall a. n a -> m a
unhoist Function m
f [(Maybe Text, GVal n)]
args =
    let args' :: [(Maybe Text, GVal m)]
args' = [ (Maybe Text
name, (forall a. n a -> m a)
-> (forall a. m a -> n a) -> GVal n -> GVal m
forall (m :: * -> *) (n :: * -> *).
(Functor m, Functor n) =>
(forall a. m a -> n a)
-> (forall a. n a -> m a) -> GVal m -> GVal n
marshalGValEx forall a. n a -> m a
unhoist forall a. m a -> n a
hoist GVal n
value)
                | (Maybe Text
name, GVal n
value) <- [(Maybe Text, GVal n)]
args
                ]
    in (forall a. m a -> n a)
-> (forall a. n a -> m a) -> GVal m -> GVal n
forall (m :: * -> *) (n :: * -> *).
(Functor m, Functor n) =>
(forall a. m a -> n a)
-> (forall a. n a -> m a) -> GVal m -> GVal n
marshalGValEx forall a. m a -> n a
hoist forall a. n a -> m a
unhoist (GVal m -> GVal n) -> n (GVal m) -> n (GVal n)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m (GVal m) -> n (GVal m)
forall a. m a -> n a
hoist (Function m
f [(Maybe Text, GVal m)]
args')

-- | Convenience wrapper around 'asDictItems' to represent a 'GVal' as a
-- 'HashMap'.
asHashMap :: GVal m -> Maybe (HashMap Text (GVal m))
asHashMap :: GVal m -> Maybe (HashMap Text (GVal m))
asHashMap GVal m
g = [(Text, GVal m)] -> HashMap Text (GVal m)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HashMap.fromList ([(Text, GVal m)] -> HashMap Text (GVal m))
-> Maybe [(Text, GVal m)] -> Maybe (HashMap Text (GVal m))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe [(Text, GVal m)]
forall (m :: * -> *). GVal m -> Maybe [(Text, GVal m)]
asDictItems GVal m
g

-- | The default 'GVal' is equivalent to NULL.
instance Default (GVal m) where
    def :: GVal m
def = GVal :: forall (m :: * -> *).
Maybe [GVal m]
-> Maybe [(Text, GVal m)]
-> Maybe (Text -> Maybe (GVal m))
-> Html
-> Text
-> Bool
-> Maybe Scientific
-> Maybe (Function m)
-> Maybe ByteString
-> Maybe Int
-> Bool
-> Maybe Value
-> GVal m
GVal
            { asList :: Maybe [GVal m]
asList = Maybe [GVal m]
forall a. Maybe a
Nothing
            , asDictItems :: Maybe [(Text, GVal m)]
asDictItems = Maybe [(Text, GVal m)]
forall a. Maybe a
Nothing
            , asLookup :: Maybe (Text -> Maybe (GVal m))
asLookup = Maybe (Text -> Maybe (GVal m))
forall a. Maybe a
Nothing
            , asHtml :: Html
asHtml = Text -> Html
unsafeRawHtml Text
""
            , asText :: Text
asText = Text
""
            , asBytes :: Maybe ByteString
asBytes = Maybe ByteString
forall a. Maybe a
Nothing
            , asBoolean :: Bool
asBoolean = Bool
False
            , asNumber :: Maybe Scientific
asNumber = Maybe Scientific
forall a. Maybe a
Nothing
            , asFunction :: Maybe (Function m)
asFunction = Maybe (Function m)
forall a. Maybe a
Nothing
            , isNull :: Bool
isNull = Bool
True
            , length :: Maybe Int
length = Maybe Int
forall a. Maybe a
Nothing
            , asJSON :: Maybe Value
asJSON = Maybe Value
forall a. Maybe a
Nothing
            }

-- | Conversion to JSON values attempts the following conversions, in order:
--
-- - check the 'isNull' property; if it is 'True', always return 'Null',
--   even if the GVal implements 'asJSON'
-- - 'asJSON'
-- - 'asList'
-- - 'asDictItems' (through 'asHashMap')
-- - 'asNumber'
-- - 'asText'
--
-- Note that the default conversions will never return booleans unless 'asJSON'
-- explicitly does this, because 'asText' will always return *something*.
instance JSON.ToJSON (GVal m) where
    toJSON :: GVal m -> Value
toJSON GVal m
g =
        if GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isNull GVal m
g
            then Value
JSON.Null
            else Value -> Maybe Value -> Value
forall a. a -> Maybe a -> a
fromMaybe (Text -> Value
forall a. ToJSON a => a -> Value
JSON.toJSON (Text -> Value) -> Text -> Value
forall a b. (a -> b) -> a -> b
$ GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
g) (Maybe Value -> Value) -> Maybe Value -> Value
forall a b. (a -> b) -> a -> b
$
                    GVal m -> Maybe Value
forall (m :: * -> *). GVal m -> Maybe Value
asJSON GVal m
g Maybe Value -> Maybe Value -> Maybe Value
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
                    ([GVal m] -> Value
forall a. ToJSON a => a -> Value
JSON.toJSON ([GVal m] -> Value) -> Maybe [GVal m] -> Maybe Value
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
g) Maybe Value -> Maybe Value -> Maybe Value
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
                    (HashMap Text (GVal m) -> Value
forall a. ToJSON a => a -> Value
JSON.toJSON (HashMap Text (GVal m) -> Value)
-> Maybe (HashMap Text (GVal m)) -> Maybe Value
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe (HashMap Text (GVal m))
forall (m :: * -> *). GVal m -> Maybe (HashMap Text (GVal m))
asHashMap GVal m
g) Maybe Value -> Maybe Value -> Maybe Value
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
                    (Scientific -> Value
forall a. ToJSON a => a -> Value
JSON.toJSON (Scientific -> Value) -> Maybe Scientific -> Maybe Value
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber GVal m
g)

-- | For convenience, 'Show' is implemented in a way that looks similar to
-- JavaScript / JSON
instance Show (GVal m) where
    show :: GVal m -> String
show GVal m
v
        | GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isNull GVal m
v = String
"null"
        | Maybe (Function m) -> Bool
forall a. Maybe a -> Bool
isJust (GVal m -> Maybe (Function m)
forall (m :: * -> *). GVal m -> Maybe (Function m)
asFunction GVal m
v) = String
"<<function>>"
        | Maybe [(Text, GVal m)] -> Bool
forall a. Maybe a -> Bool
isJust (GVal m -> Maybe [(Text, GVal m)]
forall (m :: * -> *). GVal m -> Maybe [(Text, GVal m)]
asDictItems GVal m
v) =
            let items :: [String]
items = [ Text -> String
forall a. Show a => a -> String
show Text
k String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
": " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> GVal m -> String
forall a. Show a => a -> String
show GVal m
v | (Text
k, GVal m
v) <- [(Text, GVal m)] -> Maybe [(Text, GVal m)] -> [(Text, GVal m)]
forall a. a -> Maybe a -> a
fromMaybe [] (GVal m -> Maybe [(Text, GVal m)]
forall (m :: * -> *). GVal m -> Maybe [(Text, GVal m)]
asDictItems GVal m
v) ]
                      [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [ Integer -> String
forall a. Show a => a -> String
show Integer
k String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
": " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> GVal m -> String
forall a. Show a => a -> String
show GVal m
v | (Integer
k, GVal m
v) <- [Integer] -> [GVal m] -> [(Integer, GVal m)]
forall a b. [a] -> [b] -> [(a, b)]
Prelude.zip [Integer
0..] ([GVal m] -> Maybe [GVal m] -> [GVal m]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [GVal m] -> [GVal m]) -> Maybe [GVal m] -> [GVal m]
forall a b. (a -> b) -> a -> b
$ GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
v) ]
            in String
"{" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> ([String] -> String
forall a. Monoid a => [a] -> a
mconcat ([String] -> String)
-> ([String] -> [String]) -> [String] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String] -> [String]
forall a. a -> [a] -> [a]
List.intersperse String
", " ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ [String]
items) String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"}"
        | Maybe [GVal m] -> Bool
forall a. Maybe a -> Bool
isJust (GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
v) = String
"[" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> ([String] -> String
forall a. Monoid a => [a] -> a
mconcat ([String] -> String)
-> ([GVal m] -> [String]) -> [GVal m] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String] -> [String]
forall a. a -> [a] -> [a]
List.intersperse String
", " ([String] -> [String])
-> ([GVal m] -> [String]) -> [GVal m] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GVal m -> String) -> [GVal m] -> [String]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> String
forall a. Show a => a -> String
show ([GVal m] -> String) -> [GVal m] -> String
forall a b. (a -> b) -> a -> b
$ [GVal m] -> Maybe [GVal m] -> [GVal m]
forall a. a -> Maybe a -> a
fromMaybe [] (GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
v)) String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"]"
        | Maybe Scientific -> Bool
forall a. Maybe a -> Bool
isJust (GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber GVal m
v) =
            case Scientific -> Either Double Integer
forall r i. (RealFloat r, Integral i) => Scientific -> Either r i
floatingOrInteger (Scientific -> Either Double Integer)
-> Maybe Scientific -> Maybe (Either Double Integer)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber GVal m
v :: Maybe (Either Double Integer) of
                Just (Left Double
x) -> Maybe Scientific -> String
forall a. Show a => a -> String
show (GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber GVal m
v)
                Just (Right Integer
x) -> Integer -> String
forall a. Show a => a -> String
show Integer
x
                Maybe (Either Double Integer)
Nothing -> String
""
        | Bool
otherwise = Text -> String
forall a. Show a => a -> String
show (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
v

-- | Converting to HTML hooks into the ToHtml instance for 'Text' for most tags.
-- Tags that have no obvious textual representation render as empty HTML.
instance ToHtml (GVal m) where
    toHtml :: GVal m -> Html
toHtml = GVal m -> Html
forall (m :: * -> *). GVal m -> Html
asHtml

instance PrintfArg (GVal m) where
    formatArg :: GVal m -> FieldFormatter
formatArg GVal m
x FieldFormat
fmt =
        case FieldFormat -> Char
fmtChar (Char -> FieldFormat -> FieldFormat
vFmt Char
's' FieldFormat
fmt) of
            Char
's' -> String -> FieldFormatter
forall a. IsChar a => [a] -> FieldFormatter
formatString
                    (Text -> String
Text.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
x)
                    (FieldFormat
fmt { fmtChar :: Char
fmtChar = Char
's', fmtPrecision :: Maybe Int
fmtPrecision = Maybe Int
forall a. Maybe a
Nothing })
            Char
'c' -> String -> FieldFormatter
forall a. IsChar a => [a] -> FieldFormatter
formatString
                    (Text -> String
Text.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
x)
                    FieldFormat
fmt
            Char
f -> if Char
f Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`Prelude.elem` [Char
'f', Char
'F', Char
'g', Char
'G', Char
'e', Char
'E']
                    then Double -> FieldFormatter
forall a. RealFloat a => a -> FieldFormatter
formatRealFloat (Scientific -> Double
forall a. RealFloat a => Scientific -> a
toRealFloat (Scientific -> Double)
-> (GVal m -> Scientific) -> GVal m -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Scientific -> Maybe Scientific -> Scientific
forall a. a -> Maybe a -> a
fromMaybe Scientific
0 (Maybe Scientific -> Scientific)
-> (GVal m -> Maybe Scientific) -> GVal m -> Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber (GVal m -> Double) -> GVal m -> Double
forall a b. (a -> b) -> a -> b
$ GVal m
x) FieldFormat
fmt
                    else Integer -> FieldFormatter
formatInteger (Scientific -> Integer
forall a b. (RealFrac a, Integral b) => a -> b
Prelude.round (Scientific -> Integer)
-> (GVal m -> Scientific) -> GVal m -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Scientific -> Maybe Scientific -> Scientific
forall a. a -> Maybe a -> a
fromMaybe Scientific
0 (Maybe Scientific -> Scientific)
-> (GVal m -> Maybe Scientific) -> GVal m -> Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber (GVal m -> Integer) -> GVal m -> Integer
forall a b. (a -> b) -> a -> b
$ GVal m
x) FieldFormat
fmt

-- * Representing functions as 'GVal's
--
-- | A function that can be called from within a template execution context.
type Function m = [(Maybe Text, GVal m)] -> m (GVal m)

-- | Match arguments passed to a function at runtime against a list of declared
-- argument names.
-- @matchFuncArgs argNames argsPassed@ returns @(matchedArgs, positionalArgs, namedArgs)@,
-- where @matchedArgs@ is a list of arguments matched against declared names
-- (by name or by position), @positionalArgs@ are the unused positional
-- (unnamed) arguments, and @namedArgs@ are the unused named arguments.
matchFuncArgs :: [Text] -> [(Maybe Text, GVal m)] -> (HashMap Text (GVal m), [GVal m], HashMap Text (GVal m))
matchFuncArgs :: [Text]
-> [(Maybe Text, GVal m)]
-> (HashMap Text (GVal m), [GVal m], HashMap Text (GVal m))
matchFuncArgs [Text]
names [(Maybe Text, GVal m)]
args =
    (HashMap Text (GVal m)
matched, [GVal m]
positional, HashMap Text (GVal m)
named)
    where
        positionalRaw :: [GVal m]
positionalRaw = [ GVal m
v | (Maybe Text
Nothing, GVal m
v) <- [(Maybe Text, GVal m)]
args ]
        namedRaw :: HashMap Text (GVal m)
namedRaw = [(Text, GVal m)] -> HashMap Text (GVal m)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HashMap.fromList [ (Text
n, GVal m
v) | (Just Text
n, GVal m
v) <- [(Maybe Text, GVal m)]
args ]
        fromPositional :: [(Text, GVal m)]
fromPositional = [Text] -> [GVal m] -> [(Text, GVal m)]
forall a b. [a] -> [b] -> [(a, b)]
Prelude.zip [Text]
names [GVal m]
positionalRaw
        numPositional :: Int
numPositional = [(Text, GVal m)] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
Prelude.length [(Text, GVal m)]
fromPositional
        namesRemaining :: [Text]
namesRemaining = Int -> [Text] -> [Text]
forall a. Int -> [a] -> [a]
Prelude.drop Int
numPositional [Text]
names
        positional :: [GVal m]
positional = Int -> [GVal m] -> [GVal m]
forall a. Int -> [a] -> [a]
Prelude.drop Int
numPositional [GVal m]
positionalRaw
        fromNamed :: [(Text, GVal m)]
fromNamed = (Text -> Maybe (Text, GVal m)) -> [Text] -> [(Text, GVal m)]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Text -> Maybe (Text, GVal m)
lookupName [Text]
namesRemaining
        lookupName :: Text -> Maybe (Text, GVal m)
lookupName Text
n = do
            GVal m
v <- Text -> HashMap Text (GVal m) -> Maybe (GVal m)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup Text
n HashMap Text (GVal m)
namedRaw
            (Text, GVal m) -> Maybe (Text, GVal m)
forall (m :: * -> *) a. Monad m => a -> m a
return (Text
n, GVal m
v)
        matched :: HashMap Text (GVal m)
matched = [(Text, GVal m)] -> HashMap Text (GVal m)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HashMap.fromList ([(Text, GVal m)] -> HashMap Text (GVal m))
-> [(Text, GVal m)] -> HashMap Text (GVal m)
forall a b. (a -> b) -> a -> b
$ [(Text, GVal m)]
fromPositional [(Text, GVal m)] -> [(Text, GVal m)] -> [(Text, GVal m)]
forall a. [a] -> [a] -> [a]
++ [(Text, GVal m)]
fromNamed
        named :: HashMap Text (GVal m)
named = HashMap Text (GVal m)
-> HashMap Text (GVal m) -> HashMap Text (GVal m)
forall k v w.
(Eq k, Hashable k) =>
HashMap k v -> HashMap k w -> HashMap k v
HashMap.difference HashMap Text (GVal m)
namedRaw ([(Text, GVal m)] -> HashMap Text (GVal m)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HashMap.fromList [(Text, GVal m)]
fromNamed)

-- * Marshalling from Haskell to 'GVal'
--
-- | Types that implement conversion to 'GVal'.
class ToGVal m a where
    toGVal :: a -> GVal m

-- | Trivial instance for 'GVal' itself.
instance ToGVal m (GVal m) where
    toGVal :: GVal m -> GVal m
toGVal = GVal m -> GVal m
forall a. a -> a
id

instance ToGVal m () where
    toGVal :: () -> GVal m
toGVal = GVal m -> () -> GVal m
forall a b. a -> b -> a
const GVal m
forall a. Default a => a
def

-- | 'Nothing' becomes NULL, 'Just' unwraps.
instance ToGVal m v => ToGVal m (Maybe v) where
    toGVal :: Maybe v -> GVal m
toGVal Maybe v
Nothing = GVal m
forall a. Default a => a
def { asJSON :: Maybe Value
asJSON = Value -> Maybe Value
forall a. a -> Maybe a
Just Value
JSON.Null }
    toGVal (Just v
x) = v -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal v
x

-- | Haskell lists become list-like 'GVal's
instance ToGVal m v => ToGVal m [v] where
    toGVal :: [v] -> GVal m
toGVal [v]
xs = [GVal m] -> GVal m
helper ((v -> GVal m) -> [v] -> [GVal m]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map v -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal [v]
xs)
        where
            helper :: [GVal m] -> GVal m
            helper :: [GVal m] -> GVal m
helper [GVal m]
xs =
                GVal m
forall a. Default a => a
def
                    { asHtml :: Html
asHtml = [Html] -> Html
forall a. Monoid a => [a] -> a
mconcat ([Html] -> Html) -> ([GVal m] -> [Html]) -> [GVal m] -> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GVal m -> Html) -> [GVal m] -> [Html]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> Html
forall (m :: * -> *). GVal m -> Html
asHtml ([GVal m] -> Html) -> [GVal m] -> Html
forall a b. (a -> b) -> a -> b
$ [GVal m]
xs
                    , asText :: Text
asText = [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat ([Text] -> Text) -> ([GVal m] -> [Text]) -> [GVal m] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GVal m -> Text) -> [GVal m] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText ([GVal m] -> Text) -> [GVal m] -> Text
forall a b. (a -> b) -> a -> b
$ [GVal m]
xs
                    , asBytes :: Maybe ByteString
asBytes = [Maybe ByteString] -> Maybe ByteString
forall a. Monoid a => [a] -> a
mconcat ([Maybe ByteString] -> Maybe ByteString)
-> ([GVal m] -> [Maybe ByteString]) -> [GVal m] -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GVal m -> Maybe ByteString) -> [GVal m] -> [Maybe ByteString]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> Maybe ByteString
forall (m :: * -> *). GVal m -> Maybe ByteString
asBytes ([GVal m] -> Maybe ByteString) -> [GVal m] -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ [GVal m]
xs
                    , asBoolean :: Bool
asBoolean = Bool -> Bool
not (Bool -> Bool) -> ([GVal m] -> Bool) -> [GVal m] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [GVal m] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
List.null ([GVal m] -> Bool) -> [GVal m] -> Bool
forall a b. (a -> b) -> a -> b
$ [GVal m]
xs
                    , isNull :: Bool
isNull = Bool
False
                    , asList :: Maybe [GVal m]
asList = [GVal m] -> Maybe [GVal m]
forall a. a -> Maybe a
Just ([GVal m] -> Maybe [GVal m]) -> [GVal m] -> Maybe [GVal m]
forall a b. (a -> b) -> a -> b
$ (GVal m -> GVal m) -> [GVal m] -> [GVal m]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal [GVal m]
xs
                    , length :: Maybe Int
length = Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ [GVal m] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
Prelude.length [GVal m]
xs
                    }

-- | 'HashMap' of 'Text' becomes a dictionary-like 'GVal'
instance ToGVal m v => ToGVal m (HashMap Text v) where
    toGVal :: HashMap Text v -> GVal m
toGVal HashMap Text v
xs = HashMap Text (GVal m) -> GVal m
helper ((v -> GVal m) -> HashMap Text v -> HashMap Text (GVal m)
forall v1 v2 k. (v1 -> v2) -> HashMap k v1 -> HashMap k v2
HashMap.map v -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal HashMap Text v
xs)
        where
            helper :: HashMap Text (GVal m) -> GVal m
            helper :: HashMap Text (GVal m) -> GVal m
helper HashMap Text (GVal m)
xs =
                GVal m
forall a. Default a => a
def
                    { asHtml :: Html
asHtml = [Html] -> Html
forall a. Monoid a => [a] -> a
mconcat ([Html] -> Html)
-> (HashMap Text (GVal m) -> [Html])
-> HashMap Text (GVal m)
-> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GVal m -> Html) -> [GVal m] -> [Html]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> Html
forall (m :: * -> *). GVal m -> Html
asHtml ([GVal m] -> [Html])
-> (HashMap Text (GVal m) -> [GVal m])
-> HashMap Text (GVal m)
-> [Html]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashMap Text (GVal m) -> [GVal m]
forall k v. HashMap k v -> [v]
HashMap.elems (HashMap Text (GVal m) -> Html) -> HashMap Text (GVal m) -> Html
forall a b. (a -> b) -> a -> b
$ HashMap Text (GVal m)
xs
                    , asText :: Text
asText = [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat ([Text] -> Text)
-> (HashMap Text (GVal m) -> [Text])
-> HashMap Text (GVal m)
-> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GVal m -> Text) -> [GVal m] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText ([GVal m] -> [Text])
-> (HashMap Text (GVal m) -> [GVal m])
-> HashMap Text (GVal m)
-> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashMap Text (GVal m) -> [GVal m]
forall k v. HashMap k v -> [v]
HashMap.elems (HashMap Text (GVal m) -> Text) -> HashMap Text (GVal m) -> Text
forall a b. (a -> b) -> a -> b
$ HashMap Text (GVal m)
xs
                    , asBytes :: Maybe ByteString
asBytes = [Maybe ByteString] -> Maybe ByteString
forall a. Monoid a => [a] -> a
mconcat ([Maybe ByteString] -> Maybe ByteString)
-> (HashMap Text (GVal m) -> [Maybe ByteString])
-> HashMap Text (GVal m)
-> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GVal m -> Maybe ByteString) -> [GVal m] -> [Maybe ByteString]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> Maybe ByteString
forall (m :: * -> *). GVal m -> Maybe ByteString
asBytes ([GVal m] -> [Maybe ByteString])
-> (HashMap Text (GVal m) -> [GVal m])
-> HashMap Text (GVal m)
-> [Maybe ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashMap Text (GVal m) -> [GVal m]
forall k v. HashMap k v -> [v]
HashMap.elems (HashMap Text (GVal m) -> Maybe ByteString)
-> HashMap Text (GVal m) -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ HashMap Text (GVal m)
xs
                    , asBoolean :: Bool
asBoolean = Bool -> Bool
not (Bool -> Bool)
-> (HashMap Text (GVal m) -> Bool) -> HashMap Text (GVal m) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashMap Text (GVal m) -> Bool
forall k v. HashMap k v -> Bool
HashMap.null (HashMap Text (GVal m) -> Bool) -> HashMap Text (GVal m) -> Bool
forall a b. (a -> b) -> a -> b
$ HashMap Text (GVal m)
xs
                    , isNull :: Bool
isNull = Bool
False
                    , asLookup :: Maybe (Text -> Maybe (GVal m))
asLookup = (Text -> Maybe (GVal m)) -> Maybe (Text -> Maybe (GVal m))
forall a. a -> Maybe a
Just (Text -> HashMap Text (GVal m) -> Maybe (GVal m)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
`HashMap.lookup` HashMap Text (GVal m)
xs)
                    , asDictItems :: Maybe [(Text, GVal m)]
asDictItems = [(Text, GVal m)] -> Maybe [(Text, GVal m)]
forall a. a -> Maybe a
Just ([(Text, GVal m)] -> Maybe [(Text, GVal m)])
-> [(Text, GVal m)] -> Maybe [(Text, GVal m)]
forall a b. (a -> b) -> a -> b
$ HashMap Text (GVal m) -> [(Text, GVal m)]
forall k v. HashMap k v -> [(k, v)]
HashMap.toList HashMap Text (GVal m)
xs
                    }

-- | 'Map' of 'Text' becomes a dictionary-like 'GVal'
instance ToGVal m v => ToGVal m (Map Text v) where
    toGVal :: Map Text v -> GVal m
toGVal Map Text v
xs = Map Text (GVal m) -> GVal m
helper ((v -> GVal m) -> Map Text v -> Map Text (GVal m)
forall a b k. (a -> b) -> Map k a -> Map k b
Map.map v -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal Map Text v
xs)
        where
            helper :: Map Text (GVal m) -> GVal m
            helper :: Map Text (GVal m) -> GVal m
helper Map Text (GVal m)
xs =
                GVal m
forall a. Default a => a
def
                    { asHtml :: Html
asHtml = [Html] -> Html
forall a. Monoid a => [a] -> a
mconcat ([Html] -> Html)
-> (Map Text (GVal m) -> [Html]) -> Map Text (GVal m) -> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GVal m -> Html) -> [GVal m] -> [Html]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> Html
forall (m :: * -> *). GVal m -> Html
asHtml ([GVal m] -> [Html])
-> (Map Text (GVal m) -> [GVal m]) -> Map Text (GVal m) -> [Html]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map Text (GVal m) -> [GVal m]
forall k a. Map k a -> [a]
Map.elems (Map Text (GVal m) -> Html) -> Map Text (GVal m) -> Html
forall a b. (a -> b) -> a -> b
$ Map Text (GVal m)
xs
                    , asText :: Text
asText = [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat ([Text] -> Text)
-> (Map Text (GVal m) -> [Text]) -> Map Text (GVal m) -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GVal m -> Text) -> [GVal m] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText ([GVal m] -> [Text])
-> (Map Text (GVal m) -> [GVal m]) -> Map Text (GVal m) -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map Text (GVal m) -> [GVal m]
forall k a. Map k a -> [a]
Map.elems (Map Text (GVal m) -> Text) -> Map Text (GVal m) -> Text
forall a b. (a -> b) -> a -> b
$ Map Text (GVal m)
xs
                    , asBytes :: Maybe ByteString
asBytes = [Maybe ByteString] -> Maybe ByteString
forall a. Monoid a => [a] -> a
mconcat ([Maybe ByteString] -> Maybe ByteString)
-> (Map Text (GVal m) -> [Maybe ByteString])
-> Map Text (GVal m)
-> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GVal m -> Maybe ByteString) -> [GVal m] -> [Maybe ByteString]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map GVal m -> Maybe ByteString
forall (m :: * -> *). GVal m -> Maybe ByteString
asBytes ([GVal m] -> [Maybe ByteString])
-> (Map Text (GVal m) -> [GVal m])
-> Map Text (GVal m)
-> [Maybe ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map Text (GVal m) -> [GVal m]
forall k a. Map k a -> [a]
Map.elems (Map Text (GVal m) -> Maybe ByteString)
-> Map Text (GVal m) -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Map Text (GVal m)
xs
                    , asBoolean :: Bool
asBoolean = Bool -> Bool
not (Bool -> Bool)
-> (Map Text (GVal m) -> Bool) -> Map Text (GVal m) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map Text (GVal m) -> Bool
forall k a. Map k a -> Bool
Map.null (Map Text (GVal m) -> Bool) -> Map Text (GVal m) -> Bool
forall a b. (a -> b) -> a -> b
$ Map Text (GVal m)
xs
                    , isNull :: Bool
isNull = Bool
False
                    , asLookup :: Maybe (Text -> Maybe (GVal m))
asLookup = (Text -> Maybe (GVal m)) -> Maybe (Text -> Maybe (GVal m))
forall a. a -> Maybe a
Just (Text -> Map Text (GVal m) -> Maybe (GVal m)
forall k a. Ord k => k -> Map k a -> Maybe a
`Map.lookup` Map Text (GVal m)
xs)
                    , asDictItems :: Maybe [(Text, GVal m)]
asDictItems = [(Text, GVal m)] -> Maybe [(Text, GVal m)]
forall a. a -> Maybe a
Just ([(Text, GVal m)] -> Maybe [(Text, GVal m)])
-> [(Text, GVal m)] -> Maybe [(Text, GVal m)]
forall a b. (a -> b) -> a -> b
$ Map Text (GVal m) -> [(Text, GVal m)]
forall k a. Map k a -> [(k, a)]
Map.toAscList Map Text (GVal m)
xs
                    }

instance ToGVal m Int where
    toGVal :: Int -> GVal m
toGVal Int
x =
        GVal m
forall a. Default a => a
def
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Html) -> (Int -> Text) -> Int -> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
Text.pack (String -> Text) -> (Int -> String) -> Int -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> String
forall a. Show a => a -> String
show (Int -> Html) -> Int -> Html
forall a b. (a -> b) -> a -> b
$ Int
x
            , asText :: Text
asText = String -> Text
Text.pack (String -> Text) -> (Int -> String) -> Int -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> String
forall a. Show a => a -> String
show (Int -> Text) -> Int -> Text
forall a b. (a -> b) -> a -> b
$ Int
x
            , asBoolean :: Bool
asBoolean = Int
x Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0
            , asNumber :: Maybe Scientific
asNumber = Scientific -> Maybe Scientific
forall a. a -> Maybe a
Just (Scientific -> Maybe Scientific)
-> (Int -> Scientific) -> Int -> Maybe Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Scientific
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Maybe Scientific) -> Int -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ Int
x
            , isNull :: Bool
isNull = Bool
False
            }

instance ToGVal m Integer where
    toGVal :: Integer -> GVal m
toGVal Integer
x =
        GVal m
forall a. Default a => a
def
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Html) -> (Integer -> Text) -> Integer -> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
Text.pack (String -> Text) -> (Integer -> String) -> Integer -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> String
forall a. Show a => a -> String
show (Integer -> Html) -> Integer -> Html
forall a b. (a -> b) -> a -> b
$ Integer
x
            , asText :: Text
asText = String -> Text
Text.pack (String -> Text) -> (Integer -> String) -> Integer -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> String
forall a. Show a => a -> String
show (Integer -> Text) -> Integer -> Text
forall a b. (a -> b) -> a -> b
$ Integer
x
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (Integer -> ByteString) -> Integer -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> ByteString) -> (Integer -> Text) -> Integer -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
Text.pack (String -> Text) -> (Integer -> String) -> Integer -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> String
forall a. Show a => a -> String
show (Integer -> Maybe ByteString) -> Integer -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Integer
x
            , asBoolean :: Bool
asBoolean = Integer
x Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
0
            , asNumber :: Maybe Scientific
asNumber = Scientific -> Maybe Scientific
forall a. a -> Maybe a
Just (Scientific -> Maybe Scientific)
-> (Integer -> Scientific) -> Integer -> Maybe Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Scientific
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Maybe Scientific) -> Integer -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ Integer
x
            , isNull :: Bool
isNull = Bool
False
            }

instance ToGVal m Scientific where
    toGVal :: Scientific -> GVal m
toGVal Scientific
x =
        GVal m
forall a. Default a => a
def
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Html) -> Text -> Html
forall a b. (a -> b) -> a -> b
$ Scientific -> Text
scientificToText Scientific
x
            , asText :: Text
asText = Scientific -> Text
scientificToText Scientific
x
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (Scientific -> ByteString) -> Scientific -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> ByteString)
-> (Scientific -> Text) -> Scientific -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Scientific -> Text
scientificToText (Scientific -> Maybe ByteString) -> Scientific -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Scientific
x
            , asBoolean :: Bool
asBoolean = Scientific
x Scientific -> Scientific -> Bool
forall a. Eq a => a -> a -> Bool
/= Scientific
0
            , asNumber :: Maybe Scientific
asNumber = Scientific -> Maybe Scientific
forall a. a -> Maybe a
Just Scientific
x
            , isNull :: Bool
isNull = Bool
False
            }

instance ToGVal m Day where
    toGVal :: Day -> GVal m
toGVal Day
x =
        let dayDict :: [(Text, GVal m)]
dayDict = Day -> [(Text, GVal m)]
forall (m :: * -> *). Day -> [(Text, GVal m)]
dayToDict Day
x
            julian :: Integer
julian = Day -> Integer
toModifiedJulianDay Day
x
            formatted :: Text
formatted = String -> Text
Text.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ TimeLocale -> String -> Day -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%0Y-%m-%d" Day
x
        in ([Pair m] -> GVal m
forall (m :: * -> *). [Pair m] -> GVal m
orderedDict [Pair m]
forall (m :: * -> *). [(Text, GVal m)]
dayDict)
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Html) -> Text -> Html
forall a b. (a -> b) -> a -> b
$ Text
formatted
            , asText :: Text
asText = Text
formatted
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (Text -> ByteString) -> Text -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> Maybe ByteString) -> Text -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Text
formatted
            , asBoolean :: Bool
asBoolean = Bool
True
            , asNumber :: Maybe Scientific
asNumber = Scientific -> Maybe Scientific
forall a. a -> Maybe a
Just (Scientific -> Maybe Scientific)
-> (Integer -> Scientific) -> Integer -> Maybe Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Scientific
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Maybe Scientific) -> Integer -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ Integer
julian
            , asList :: Maybe [GVal m]
asList = [GVal m] -> Maybe [GVal m]
forall a. a -> Maybe a
Just ((Pair m -> GVal m) -> [Pair m] -> [GVal m]
forall a b. (a -> b) -> [a] -> [b]
List.map Pair m -> GVal m
forall a b. (a, b) -> b
snd [Pair m]
forall (m :: * -> *). [(Text, GVal m)]
dayDict)
            }

dayToDict :: Day -> [(Text, GVal m)]
dayToDict :: Day -> [(Text, GVal m)]
dayToDict Day
x =
    let (Integer
year, Int
month, Int
day) = Day -> (Integer, Int, Int)
toGregorian Day
x
    in [ Text
"year" Text -> Integer -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> Integer
year
        , Text
"month" Text -> Int -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> Int
month
        , Text
"day" Text -> Int -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> Int
day
        ]

instance ToGVal m TimeOfDay where
    toGVal :: TimeOfDay -> GVal m
toGVal TimeOfDay
x =
        let timeDict :: [(Text, GVal m)]
timeDict = TimeOfDay -> [(Text, GVal m)]
forall (m :: * -> *). TimeOfDay -> [(Text, GVal m)]
timeToDict TimeOfDay
x
            formatted :: Text
formatted = String -> Text
Text.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ TimeLocale -> String -> TimeOfDay -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%H:%M:%S" TimeOfDay
x
        in ([Pair m] -> GVal m
forall (m :: * -> *). [Pair m] -> GVal m
orderedDict [Pair m]
forall (m :: * -> *). [(Text, GVal m)]
timeDict)
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Html) -> Text -> Html
forall a b. (a -> b) -> a -> b
$ Text
formatted
            , asText :: Text
asText = Text
formatted
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (Text -> ByteString) -> Text -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> Maybe ByteString) -> Text -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Text
formatted
            , asBoolean :: Bool
asBoolean = Bool
True
            , asNumber :: Maybe Scientific
asNumber = Maybe Scientific
forall a. Maybe a
Nothing
            , asList :: Maybe [GVal m]
asList = [GVal m] -> Maybe [GVal m]
forall a. a -> Maybe a
Just ((Pair m -> GVal m) -> [Pair m] -> [GVal m]
forall a b. (a -> b) -> [a] -> [b]
List.map Pair m -> GVal m
forall a b. (a, b) -> b
snd [Pair m]
forall (m :: * -> *). [(Text, GVal m)]
timeDict)
            }

timeToDict :: TimeOfDay -> [(Text, GVal m)]
timeToDict :: TimeOfDay -> [(Text, GVal m)]
timeToDict (TimeOfDay Int
hours Int
minutes Pico
seconds) =
    [ Text
"hours" Text -> Int -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> Int
hours
    , Text
"minutes" Text -> Int -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> Int
minutes
    , Text
"seconds" Text -> Scientific -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> Pico -> Scientific
picoToScientific Pico
seconds
    ]

instance ToGVal m LocalTime where
    toGVal :: LocalTime -> GVal m
toGVal LocalTime
x =
        let dtDict :: [(Text, GVal m)]
dtDict = LocalTime -> [(Text, GVal m)]
forall (m :: * -> *). LocalTime -> [(Text, GVal m)]
localTimeToDict LocalTime
x
            formatted :: Text
formatted = String -> Text
Text.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ TimeLocale -> String -> LocalTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%0Y-%m-%d %H:%M:%S" LocalTime
x
        in ([Pair m] -> GVal m
forall (m :: * -> *). [Pair m] -> GVal m
orderedDict ([Pair m] -> GVal m) -> [Pair m] -> GVal m
forall a b. (a -> b) -> a -> b
$
                [Pair m]
forall (m :: * -> *). [(Text, GVal m)]
dtDict [Pair m] -> [Pair m] -> [Pair m]
forall a. [a] -> [a] -> [a]
++
                [ Text
"date" Text -> Day -> Pair m
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> LocalTime -> Day
localDay LocalTime
x
                , Text
"time" Text -> TimeOfDay -> Pair m
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> LocalTime -> TimeOfDay
localTimeOfDay LocalTime
x
                ])
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Html) -> Text -> Html
forall a b. (a -> b) -> a -> b
$ Text
formatted
            , asText :: Text
asText = Text
formatted
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (Text -> ByteString) -> Text -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> Maybe ByteString) -> Text -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Text
formatted
            , asBoolean :: Bool
asBoolean = Bool
True
            , asNumber :: Maybe Scientific
asNumber = Maybe Scientific
forall a. Maybe a
Nothing
            , asList :: Maybe [GVal m]
asList = [GVal m] -> Maybe [GVal m]
forall a. a -> Maybe a
Just ((Pair m -> GVal m) -> [Pair m] -> [GVal m]
forall a b. (a -> b) -> [a] -> [b]
List.map Pair m -> GVal m
forall a b. (a, b) -> b
snd [Pair m]
forall (m :: * -> *). [(Text, GVal m)]
dtDict)
            }

localTimeToDict :: LocalTime -> [(Text, GVal m)]
localTimeToDict :: LocalTime -> [(Text, GVal m)]
localTimeToDict LocalTime
x =
        let dayDict :: [(Text, GVal m)]
dayDict = Day -> [(Text, GVal m)]
forall (m :: * -> *). Day -> [(Text, GVal m)]
dayToDict (Day -> [(Text, GVal m)]) -> Day -> [(Text, GVal m)]
forall a b. (a -> b) -> a -> b
$ LocalTime -> Day
localDay LocalTime
x
            timeDict :: [(Text, GVal m)]
timeDict = TimeOfDay -> [(Text, GVal m)]
forall (m :: * -> *). TimeOfDay -> [(Text, GVal m)]
timeToDict (TimeOfDay -> [(Text, GVal m)]) -> TimeOfDay -> [(Text, GVal m)]
forall a b. (a -> b) -> a -> b
$ LocalTime -> TimeOfDay
localTimeOfDay LocalTime
x
        in [(Text, GVal m)]
forall (m :: * -> *). [(Text, GVal m)]
dayDict [(Text, GVal m)] -> [(Text, GVal m)] -> [(Text, GVal m)]
forall a. [a] -> [a] -> [a]
++ [(Text, GVal m)]
forall (m :: * -> *). [(Text, GVal m)]
timeDict

instance ToGVal m TimeZone where
    toGVal :: TimeZone -> GVal m
toGVal  = [Pair m] -> GVal m
forall (m :: * -> *). [Pair m] -> GVal m
dict ([Pair m] -> GVal m)
-> (TimeZone -> [Pair m]) -> TimeZone -> GVal m
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeZone -> [Pair m]
forall (m :: * -> *). TimeZone -> [(Text, GVal m)]
timeZoneToDict

timeZoneToDict :: TimeZone -> [(Text, GVal m)]
timeZoneToDict :: TimeZone -> [(Text, GVal m)]
timeZoneToDict (TimeZone Int
minutes Bool
summerOnly String
name) =
    [ Text
"minutes" Text -> Int -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> Int
minutes
    , Text
"summerOnly" Text -> Bool -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> Bool
summerOnly
    , Text
"name" Text -> String -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> String
name
    ]

instance ToGVal m TimeLocale where
    toGVal :: TimeLocale -> GVal m
toGVal TimeLocale
t =
        let formattedExample :: Text
formattedExample =
                String -> Text
Text.pack (String -> Text) -> (LocalTime -> String) -> LocalTime -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeLocale -> String -> LocalTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
t String
"%c" (LocalTime -> Text) -> LocalTime -> Text
forall a b. (a -> b) -> a -> b
$
                    Day -> TimeOfDay -> LocalTime
LocalTime (Integer -> Int -> Int -> Day
fromGregorian Integer
2000 Int
1 Int
1) (Int -> Int -> Pico -> TimeOfDay
TimeOfDay Int
13 Int
15 Pico
00)
            timeLocaleDict :: [(Text, GVal m)]
timeLocaleDict = TimeLocale -> [(Text, GVal m)]
forall (m :: * -> *). TimeLocale -> [(Text, GVal m)]
timeLocaleToDict TimeLocale
t
        in ([Pair m] -> GVal m
forall (m :: * -> *). [Pair m] -> GVal m
dict [Pair m]
forall (m :: * -> *). [(Text, GVal m)]
timeLocaleDict)
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Html) -> Text -> Html
forall a b. (a -> b) -> a -> b
$ Text
formattedExample
            , asText :: Text
asText = Text
formattedExample
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (Text -> ByteString) -> Text -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> Maybe ByteString) -> Text -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Text
formattedExample
            , asBoolean :: Bool
asBoolean = Bool
True
            , asNumber :: Maybe Scientific
asNumber = Maybe Scientific
forall a. Maybe a
Nothing
            }

timeLocaleToDict :: TimeLocale -> [(Text, GVal m)]
timeLocaleToDict :: TimeLocale -> [(Text, GVal m)]
timeLocaleToDict TimeLocale
t =
    [ Text
"wDays" Text -> [(Text, Text)] -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> ((String, String) -> (Text, Text))
-> [(String, String)] -> [(Text, Text)]
forall a b. (a -> b) -> [a] -> [b]
List.map (String, String) -> (Text, Text)
packPair (TimeLocale -> [(String, String)]
wDays TimeLocale
t)
    , Text
"months" Text -> [(Text, Text)] -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> ((String, String) -> (Text, Text))
-> [(String, String)] -> [(Text, Text)]
forall a b. (a -> b) -> [a] -> [b]
List.map (String, String) -> (Text, Text)
packPair (TimeLocale -> [(String, String)]
months TimeLocale
t)
    , Text
"amPm" Text -> (Text, Text) -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> (String, String) -> (Text, Text)
packPair (TimeLocale -> (String, String)
amPm TimeLocale
t)
    , Text
"dateTimeFmt" Text -> Text -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> String -> Text
Text.pack (TimeLocale -> String
dateTimeFmt TimeLocale
t)
    , Text
"dateFmt" Text -> Text -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> String -> Text
Text.pack (TimeLocale -> String
dateFmt TimeLocale
t)
    , Text
"timeFmt" Text -> Text -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> String -> Text
Text.pack (TimeLocale -> String
timeFmt TimeLocale
t)
    , Text
"time12Fmt" Text -> Text -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> String -> Text
Text.pack (TimeLocale -> String
time12Fmt TimeLocale
t)
    -- TODO
    -- , "knownTimeZones" ~> knownTimeZones t
    , Text
"knownTimeZones" Text -> [Text] -> (Text, GVal m)
forall (m :: * -> *) a. ToGVal m a => Text -> a -> Pair m
~> ([] :: [Text])
    ]

-- TODO: ToGVal instance for ZonedTime
instance ToGVal m ZonedTime where
    toGVal :: ZonedTime -> GVal m
toGVal ZonedTime
x =
        let dtDict :: [(Text, GVal m)]
dtDict = ZonedTime -> [(Text, GVal m)]
forall (m :: * -> *). ZonedTime -> [(Text, GVal m)]
zonedTimeToDict ZonedTime
x
            formatted :: Text
formatted = String -> Text
Text.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ TimeLocale -> String -> ZonedTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%0Y-%m-%d %H:%M:%S%z" ZonedTime
x
        in ([Pair m] -> GVal m
forall (m :: * -> *). [Pair m] -> GVal m
dict [Pair m]
forall (m :: * -> *). [(Text, GVal m)]
dtDict)
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Html) -> Text -> Html
forall a b. (a -> b) -> a -> b
$ Text
formatted
            , asText :: Text
asText = Text
formatted
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (Text -> ByteString) -> Text -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> Maybe ByteString) -> Text -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Text
formatted
            , asBoolean :: Bool
asBoolean = Bool
True
            , asNumber :: Maybe Scientific
asNumber = Maybe Scientific
forall a. Maybe a
Nothing
            }

zonedTimeToDict :: ZonedTime -> [(Text, GVal m)]
zonedTimeToDict :: ZonedTime -> [(Text, GVal m)]
zonedTimeToDict ZonedTime
t =
    (Text
"tz", TimeZone -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal (TimeZone -> GVal m) -> TimeZone -> GVal m
forall a b. (a -> b) -> a -> b
$ ZonedTime -> TimeZone
zonedTimeZone ZonedTime
t)(Text, GVal m) -> [(Text, GVal m)] -> [(Text, GVal m)]
forall a. a -> [a] -> [a]
:LocalTime -> [(Text, GVal m)]
forall (m :: * -> *). LocalTime -> [(Text, GVal m)]
localTimeToDict (ZonedTime -> LocalTime
zonedTimeToLocalTime ZonedTime
t)

instance (ToGVal m a, ToGVal m b) => ToGVal m (a, b) where
    toGVal :: (a, b) -> GVal m
toGVal (a
a, b
b) = [GVal m] -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal ([ a -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal a
a, b -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal b
b ] :: [GVal m])

instance (ToGVal m a, ToGVal m b, ToGVal m c) => ToGVal m (a, b, c) where
    toGVal :: (a, b, c) -> GVal m
toGVal (a
a, b
b, c
c) = [GVal m] -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal ([ a -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal a
a, b -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal b
b, c -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal c
c ] :: [GVal m])

instance (ToGVal m a, ToGVal m b, ToGVal m c, ToGVal m d) => ToGVal m (a, b, c, d) where
    toGVal :: (a, b, c, d) -> GVal m
toGVal (a
a, b
b, c
c, d
d) = [GVal m] -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal ([ a -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal a
a, b -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal b
b, c -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal c
c, d -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal d
d ] :: [GVal m])

-- | Silly helper function, needed to bypass the default 'Show' instance of
-- 'Scientific' in order to make integral 'Scientific's look like integers.
scientificToText :: Scientific -> Text
scientificToText :: Scientific -> Text
scientificToText Scientific
x =
    String -> Text
Text.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ case Scientific -> Either Double Integer
forall r i. (RealFloat r, Integral i) => Scientific -> Either r i
floatingOrInteger Scientific
x of
        Left Double
x -> Double -> String
forall a. Show a => a -> String
show Double
x
        Right Integer
x -> Integer -> String
forall a. Show a => a -> String
show Integer
x

-- | Booleans render as 1 or empty string, and otherwise behave as expected.
instance ToGVal m Bool where
    toGVal :: Bool -> GVal m
toGVal Bool
x =
        GVal m
forall a. Default a => a
def
            { asHtml :: Html
asHtml = if Bool
x then Text -> Html
html Text
"1" else Text -> Html
html Text
""
            , asText :: Text
asText = if Bool
x then Text
"1" else Text
""
            , asBoolean :: Bool
asBoolean = Bool
x
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString) -> ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ if Bool
x then ByteString
"1" else ByteString
"0"
            , asNumber :: Maybe Scientific
asNumber = Scientific -> Maybe Scientific
forall a. a -> Maybe a
Just (Scientific -> Maybe Scientific) -> Scientific -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ if Bool
x then Scientific
1 else Scientific
0
            , isNull :: Bool
isNull = Bool
False
            , asJSON :: Maybe Value
asJSON = Value -> Maybe Value
forall a. a -> Maybe a
Just (Bool -> Value
JSON.Bool Bool
x)
            }

-- | 'String' -> 'GVal' conversion uses the 'IsString' class; because 'String'
-- is an alias for '[Char]', there is also a 'ToGVal' instance for 'String',
-- but it marshals strings as lists of characters, i.e., calling 'toGVal' on
-- a string produces a list of characters on the 'GVal' side.
instance IsString (GVal m) where
    fromString :: String -> GVal m
fromString String
x =
        GVal m
forall a. Default a => a
def
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Html) -> (String -> Text) -> String -> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
Text.pack (String -> Html) -> String -> Html
forall a b. (a -> b) -> a -> b
$ String
x
            , asText :: Text
asText = String -> Text
Text.pack String
x
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (String -> ByteString) -> String -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> ByteString) -> (String -> Text) -> String -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
Text.pack (String -> Maybe ByteString) -> String -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ String
x
            , asBoolean :: Bool
asBoolean = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
Prelude.null String
x
            , asNumber :: Maybe Scientific
asNumber = String -> Maybe Scientific
forall a. Read a => String -> Maybe a
readMay String
x
            , isNull :: Bool
isNull = Bool
False
            , length :: Maybe Int
length = Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> (String -> Int) -> String -> Maybe Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
Prelude.length (String -> Maybe Int) -> String -> Maybe Int
forall a b. (a -> b) -> a -> b
$ String
x
            }

-- | Single characters are treated as length-1 'Text's.
instance ToGVal m Char where
    toGVal :: Char -> GVal m
toGVal = Text -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal (Text -> GVal m) -> (Char -> Text) -> Char -> GVal m
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Text
Text.singleton

instance ToGVal m Text where
    toGVal :: Text -> GVal m
toGVal Text
x =
        GVal m
forall a. Default a => a
def
            { asHtml :: Html
asHtml = Text -> Html
html Text
x
            , asText :: Text
asText = Text
x
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (Text -> ByteString) -> Text -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> Maybe ByteString) -> Text -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Text
x
            , asBoolean :: Bool
asBoolean = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Text -> Bool
Text.null Text
x
            , asNumber :: Maybe Scientific
asNumber = String -> Maybe Scientific
forall a. Read a => String -> Maybe a
readMay (String -> Maybe Scientific)
-> (Text -> String) -> Text -> Maybe Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
Text.unpack (Text -> Maybe Scientific) -> Text -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ Text
x
            , isNull :: Bool
isNull = Bool
False
            }

instance ToGVal m LText.Text where
    toGVal :: Text -> GVal m
toGVal Text
x =
        GVal m
forall a. Default a => a
def
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Text
LText.toStrict Text
x)
            , asText :: Text
asText = Text -> Text
LText.toStrict Text
x
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (Text -> ByteString) -> Text -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
LBS.toStrict (ByteString -> ByteString)
-> (Text -> ByteString) -> Text -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
LText.encodeUtf8 (Text -> Maybe ByteString) -> Text -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Text
x
            , asBoolean :: Bool
asBoolean = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Text -> Bool
LText.null Text
x
            , asNumber :: Maybe Scientific
asNumber = String -> Maybe Scientific
forall a. Read a => String -> Maybe a
readMay (String -> Maybe Scientific)
-> (Text -> String) -> Text -> Maybe Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
LText.unpack (Text -> Maybe Scientific) -> Text -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ Text
x
            , isNull :: Bool
isNull = Bool
False
            }

instance ToGVal m ByteString where
    toGVal :: ByteString -> GVal m
toGVal ByteString
x =
        GVal m
forall a. Default a => a
def
            { asHtml :: Html
asHtml = Text -> Html
html (ByteString -> Text
decodeUtf8 ByteString
x)
            , asText :: Text
asText = ByteString -> Text
decodeUtf8 ByteString
x
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
x
            , asBoolean :: Bool
asBoolean = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ ByteString -> Bool
BS.null ByteString
x
            , asNumber :: Maybe Scientific
asNumber = String -> Maybe Scientific
forall a. Read a => String -> Maybe a
readMay (String -> Maybe Scientific)
-> (ByteString -> String) -> ByteString -> Maybe Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
Text.unpack (Text -> String) -> (ByteString -> Text) -> ByteString -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
decodeUtf8 (ByteString -> Maybe Scientific) -> ByteString -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ ByteString
x
            , isNull :: Bool
isNull = Bool
False
            }

instance ToGVal m LBS.ByteString where
    toGVal :: ByteString -> GVal m
toGVal ByteString
x =
        GVal m
forall a. Default a => a
def
            { asHtml :: Html
asHtml = Text -> Html
html (Text -> Html) -> (ByteString -> Text) -> ByteString -> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
LText.toStrict (Text -> Text) -> (ByteString -> Text) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
LText.decodeUtf8 (ByteString -> Html) -> ByteString -> Html
forall a b. (a -> b) -> a -> b
$ ByteString
x
            , asText :: Text
asText = Text -> Text
LText.toStrict (Text -> Text) -> (ByteString -> Text) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
LText.decodeUtf8 (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString
x
            , asBytes :: Maybe ByteString
asBytes = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (ByteString -> ByteString) -> ByteString -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
LBS.toStrict (ByteString -> Maybe ByteString) -> ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ ByteString
x
            , asBoolean :: Bool
asBoolean = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ ByteString -> Bool
LBS.null ByteString
x
            , asNumber :: Maybe Scientific
asNumber = String -> Maybe Scientific
forall a. Read a => String -> Maybe a
readMay (String -> Maybe Scientific)
-> (ByteString -> String) -> ByteString -> Maybe Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
LText.unpack (Text -> String) -> (ByteString -> Text) -> ByteString -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
LText.decodeUtf8 (ByteString -> Maybe Scientific) -> ByteString -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ ByteString
x
            , isNull :: Bool
isNull = Bool
False
            }
--
-- | This instance is slightly wrong; the 'asBoolean', 'asNumber', and 'asText'
-- methods all treat the HTML source as plain text. We do this to avoid parsing
-- the HTML back into a 'Text' (and dealing with possible parser errors); the
-- reason this instance exists at all is that we still want to be able to pass
-- pre-rendered HTML around sometimes, and as long as we don't call any numeric
-- or string functions on it, everything is fine. When such HTML values
-- accidentally do get used as strings, the HTML source will bleed into the
-- visible text, but at least this will not introduce an XSS vulnerability.
--
-- It is therefore recommended to avoid passing 'Html' values into templates,
-- and also to avoid calling any string functions on 'Html' values inside
-- templates (e.g. capturing macro output and then passing it through a textual
-- filter).
instance ToGVal m Html where
    toGVal :: Html -> GVal m
toGVal Html
x =
        GVal m
forall a. Default a => a
def
            { asHtml :: Html
asHtml = Html
x
            , asText :: Text
asText = Html -> Text
htmlSource Html
x
            , asBoolean :: Bool
asBoolean = Bool -> Bool
not (Bool -> Bool) -> (Html -> Bool) -> Html -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Bool
Text.null (Text -> Bool) -> (Html -> Text) -> Html -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Html -> Text
htmlSource (Html -> Bool) -> Html -> Bool
forall a b. (a -> b) -> a -> b
$ Html
x
            , asNumber :: Maybe Scientific
asNumber = String -> Maybe Scientific
forall a. Read a => String -> Maybe a
readMay (String -> Maybe Scientific)
-> (Html -> String) -> Html -> Maybe Scientific
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
Text.unpack (Text -> String) -> (Html -> Text) -> Html -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Html -> Text
htmlSource (Html -> Maybe Scientific) -> Html -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ Html
x
            , isNull :: Bool
isNull = Bool
False
            }

-- | Convert Aeson 'Value's to 'GVal's over an arbitrary host monad. Because
-- JSON cannot represent functions, this conversion will never produce a
-- 'Function'. Further, the 'ToJSON' instance for such a 'GVal' will always
-- produce the exact 'Value' that was use to construct the it.
instance ToGVal m JSON.Value where
    toGVal :: Value -> GVal m
toGVal Value
j = (Value -> GVal m
forall (m :: * -> *). Value -> GVal m
rawJSONToGVal Value
j) { asJSON :: Maybe Value
asJSON = Value -> Maybe Value
forall a. a -> Maybe a
Just Value
j }

rawJSONToGVal :: JSON.Value -> GVal m
rawJSONToGVal :: Value -> GVal m
rawJSONToGVal (JSON.Number Scientific
n) = Scientific -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal Scientific
n
rawJSONToGVal (JSON.String Text
s) = Text -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal Text
s
rawJSONToGVal (JSON.Bool Bool
b) = Bool -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal Bool
b
rawJSONToGVal Value
JSON.Null = GVal m
forall a. Default a => a
def
rawJSONToGVal (JSON.Array Array
a) = [Value] -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal ([Value] -> GVal m) -> [Value] -> GVal m
forall a b. (a -> b) -> a -> b
$ Array -> [Value]
forall a. Vector a -> [a]
Vector.toList Array
a
rawJSONToGVal (JSON.Object Object
o) = Object -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal Object
o

-- | Turn a 'Function' into a 'GVal'
fromFunction :: Function m -> GVal m
fromFunction :: Function m -> GVal m
fromFunction Function m
f =
    GVal m
forall a. Default a => a
def
        { asHtml :: Html
asHtml = Text -> Html
html Text
""
        , asText :: Text
asText = Text
""
        , asBoolean :: Bool
asBoolean = Bool
True
        , isNull :: Bool
isNull = Bool
False
        , asFunction :: Maybe (Function m)
asFunction = Function m -> Maybe (Function m)
forall a. a -> Maybe a
Just Function m
f
        , asJSON :: Maybe Value
asJSON = Value -> Maybe Value
forall a. a -> Maybe a
Just Value
"<<function>>"
        }


-- * Convenience API for constructing heterogenous dictionaries.
--
-- Example usage:
--
-- > context :: GVal m
-- > context = dict [ "number" ~> (15 :: Int), "name" ~> ("Joe" :: String) ]

-- | A key/value pair, used for constructing dictionary GVals using a
-- compact syntax.
type Pair m = (Text, GVal m)

-- | Construct a dictionary GVal from a list of pairs. Internally, this uses
-- a hashmap, so element order will not be preserved.
dict :: [Pair m] -> GVal m
dict :: [Pair m] -> GVal m
dict = HashMap Text (GVal m) -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal (HashMap Text (GVal m) -> GVal m)
-> ([Pair m] -> HashMap Text (GVal m)) -> [Pair m] -> GVal m
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Pair m] -> HashMap Text (GVal m)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HashMap.fromList

-- | Construct an ordered dictionary GVal from a list of pairs. Internally,
-- this conversion uses both a hashmap (for O(1) lookup) and the original list,
-- so element order is preserved, but there is a bit of a memory overhead.
orderedDict :: [Pair m] -> GVal m
orderedDict :: [Pair m] -> GVal m
orderedDict [Pair m]
xs =
    GVal m
forall a. Default a => a
def
        { asHtml :: Html
asHtml = [Html] -> Html
forall a. Monoid a => [a] -> a
mconcat ([Html] -> Html) -> ([Pair m] -> [Html]) -> [Pair m] -> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Pair m -> Html) -> [Pair m] -> [Html]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map (GVal m -> Html
forall (m :: * -> *). GVal m -> Html
asHtml (GVal m -> Html) -> (Pair m -> GVal m) -> Pair m -> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pair m -> GVal m
forall a b. (a, b) -> b
snd) ([Pair m] -> Html) -> [Pair m] -> Html
forall a b. (a -> b) -> a -> b
$ [Pair m]
xs
        , asText :: Text
asText = [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat ([Text] -> Text) -> ([Pair m] -> [Text]) -> [Pair m] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Pair m -> Text) -> [Pair m] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map (GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText (GVal m -> Text) -> (Pair m -> GVal m) -> Pair m -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pair m -> GVal m
forall a b. (a, b) -> b
snd) ([Pair m] -> Text) -> [Pair m] -> Text
forall a b. (a -> b) -> a -> b
$ [Pair m]
xs
        , asBoolean :: Bool
asBoolean = Bool -> Bool
not (Bool -> Bool) -> ([Pair m] -> Bool) -> [Pair m] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Pair m] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
Prelude.null ([Pair m] -> Bool) -> [Pair m] -> Bool
forall a b. (a -> b) -> a -> b
$ [Pair m]
xs
        , isNull :: Bool
isNull = Bool
False
        , asLookup :: Maybe (Text -> Maybe (GVal m))
asLookup = (Text -> Maybe (GVal m)) -> Maybe (Text -> Maybe (GVal m))
forall a. a -> Maybe a
Just (Text -> HashMap Text (GVal m) -> Maybe (GVal m)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
`HashMap.lookup` HashMap Text (GVal m)
hm)
        , asDictItems :: Maybe [Pair m]
asDictItems = [Pair m] -> Maybe [Pair m]
forall a. a -> Maybe a
Just [Pair m]
xs
        }
    where
        hm :: HashMap Text (GVal m)
hm = [Pair m] -> HashMap Text (GVal m)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HashMap.fromList [Pair m]
xs

-- | Construct a pair from a key and a value.
(~>) :: ToGVal m a => Text -> a -> Pair m
Text
k ~> :: Text -> a -> Pair m
~> a
v = (Text
k, a -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal a
v)
infixr 8 ~>

-- * Convenience API for constructing heterogenous lists

type Cons m = [GVal m]

-- | Alias for '(~:)'.
gcons :: ToGVal m a => a -> Cons m -> Cons m
gcons :: a -> Cons m -> Cons m
gcons = (:) (GVal m -> Cons m -> Cons m)
-> (a -> GVal m) -> a -> Cons m -> Cons m
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal

-- | This operator allows constructing heterogenous lists using cons-style
-- syntax, e.g.:
--
-- >>> asText $ list ("Found " ~: (6 :: Int) ~: " items" ~: [] :: [GVal IO])
-- "Found 6 items"
(~:) :: ToGVal m a => a -> Cons m -> Cons m
~: :: a -> Cons m -> Cons m
(~:) = a -> Cons m -> Cons m
forall (m :: * -> *) a. ToGVal m a => a -> Cons m -> Cons m
gcons
infixr 5 ~:

-- | Construct a GVal from a list of GVals. This is equivalent to the 'toGVal'
-- implementation of @[GVal m]@, but typed more narrowly for clarity and
-- disambiguation.
list :: Cons m -> GVal m
list :: Cons m -> GVal m
list = Cons m -> GVal m
forall (m :: * -> *) a. ToGVal m a => a -> GVal m
toGVal

-- * Inspecting 'GVal's / Marshalling 'GVal' to Haskell

-- | Check if the given GVal is a list-like object
isList :: GVal m -> Bool
isList :: GVal m -> Bool
isList = Maybe [GVal m] -> Bool
forall a. Maybe a -> Bool
isJust (Maybe [GVal m] -> Bool)
-> (GVal m -> Maybe [GVal m]) -> GVal m -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList

-- | Check if the given GVal is a dictionary-like object
isDict :: GVal m -> Bool
isDict :: GVal m -> Bool
isDict = Maybe [(Text, GVal m)] -> Bool
forall a. Maybe a -> Bool
isJust (Maybe [(Text, GVal m)] -> Bool)
-> (GVal m -> Maybe [(Text, GVal m)]) -> GVal m -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Maybe [(Text, GVal m)]
forall (m :: * -> *). GVal m -> Maybe [(Text, GVal m)]
asDictItems

-- | Treat a 'GVal' as a flat list and look up a value by integer index.
-- If the value is not a List, or if the index exceeds the list length,
-- return 'Nothing'.
lookupIndex :: Int -> GVal m -> Maybe (GVal m)
lookupIndex :: Int -> GVal m -> Maybe (GVal m)
lookupIndex = Maybe Int -> GVal m -> Maybe (GVal m)
forall (m :: * -> *). Maybe Int -> GVal m -> Maybe (GVal m)
lookupIndexMay (Maybe Int -> GVal m -> Maybe (GVal m))
-> (Int -> Maybe Int) -> Int -> GVal m -> Maybe (GVal m)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Maybe Int
forall a. a -> Maybe a
Just

-- | Helper function; look up a value by an integer index when the index may or
-- may not be available. If no index is given, return 'Nothing'.
lookupIndexMay :: Maybe Int -> GVal m -> Maybe (GVal m)
lookupIndexMay :: Maybe Int -> GVal m -> Maybe (GVal m)
lookupIndexMay Maybe Int
i GVal m
v = do
    Int
index <- Maybe Int
i
    [GVal m]
items <- GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
v
    [GVal m] -> Int -> Maybe (GVal m)
forall a. [a] -> Int -> Maybe a
atMay [GVal m]
items Int
index

-- | Strictly-typed lookup: treat value as a dictionary-like object and look
-- up the value at a given key.
lookupKey :: Text -> GVal m -> Maybe (GVal m)
lookupKey :: Text -> GVal m -> Maybe (GVal m)
lookupKey Text
k GVal m
v = do
    Text -> Maybe (GVal m)
lf <- GVal m -> Maybe (Text -> Maybe (GVal m))
forall (m :: * -> *). GVal m -> Maybe (Text -> Maybe (GVal m))
asLookup GVal m
v
    Text -> Maybe (GVal m)
lf Text
k

-- | Loosely-typed lookup: try dictionary-style lookup first (treat index as
-- a string, and container as a dictionary), if that doesn't yield anything
-- (either because the index is not string-ish, or because the container
-- doesn't provide dictionary-style access), try index-based lookup.
lookupLoose :: GVal m -> GVal m -> Maybe (GVal m)
lookupLoose :: GVal m -> GVal m -> Maybe (GVal m)
lookupLoose GVal m
k GVal m
v =
    Text -> GVal m -> Maybe (GVal m)
forall (m :: * -> *). Text -> GVal m -> Maybe (GVal m)
lookupKey (GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText GVal m
k) GVal m
v Maybe (GVal m) -> Maybe (GVal m) -> Maybe (GVal m)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe Int -> GVal m -> Maybe (GVal m)
forall (m :: * -> *). Maybe Int -> GVal m -> Maybe (GVal m)
lookupIndexMay (Scientific -> Int
forall a b. (RealFrac a, Integral b) => a -> b
floor (Scientific -> Int) -> Maybe Scientific -> Maybe Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber GVal m
k) GVal m
v

-- | Like 'lookupLoose', but fall back to the given default value if
-- the key is not in the dictionary, or if the indexee is not a
-- dictionary-like object.
lookupLooseDef :: GVal m -> GVal m -> GVal m -> GVal m
lookupLooseDef :: GVal m -> GVal m -> GVal m -> GVal m
lookupLooseDef GVal m
d GVal m
k = GVal m -> Maybe (GVal m) -> GVal m
forall a. a -> Maybe a -> a
fromMaybe GVal m
d (Maybe (GVal m) -> GVal m)
-> (GVal m -> Maybe (GVal m)) -> GVal m -> GVal m
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> GVal m -> Maybe (GVal m)
forall (m :: * -> *). GVal m -> GVal m -> Maybe (GVal m)
lookupLoose GVal m
k

(~!) :: (FromGVal m v) => GVal m -> GVal m -> Maybe v
GVal m
g ~! :: GVal m -> GVal m -> Maybe v
~! GVal m
k = GVal m -> GVal m -> Maybe (GVal m)
forall (m :: * -> *). GVal m -> GVal m -> Maybe (GVal m)
lookupLoose GVal m
k GVal m
g Maybe (GVal m) -> (GVal m -> Maybe v) -> Maybe v
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= GVal m -> Maybe v
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal

-- | Treat a 'GVal' as a dictionary and list all the keys, with no particular
-- ordering.
keys :: GVal m -> Maybe [Text]
keys :: GVal m -> Maybe [Text]
keys GVal m
v = ((Text, GVal m) -> Text) -> [(Text, GVal m)] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map (Text, GVal m) -> Text
forall a b. (a, b) -> a
fst ([(Text, GVal m)] -> [Text])
-> Maybe [(Text, GVal m)] -> Maybe [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe [(Text, GVal m)]
forall (m :: * -> *). GVal m -> Maybe [(Text, GVal m)]
asDictItems GVal m
v

-- | Convert a 'GVal' to a number.
toNumber :: GVal m -> Maybe Scientific
toNumber :: GVal m -> Maybe Scientific
toNumber = GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber

-- | Convert a 'GVal' to an 'Int'.
-- The conversion will fail when the value is not numeric, and also if
-- it is too large to fit in an 'Int'.
toInt :: GVal m -> Maybe Int
toInt :: GVal m -> Maybe Int
toInt = Scientific -> Maybe Int
forall i. (Integral i, Bounded i) => Scientific -> Maybe i
toBoundedInteger (Scientific -> Maybe Int)
-> (GVal m -> Maybe Scientific) -> GVal m -> Maybe Int
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
toNumber

-- | Convert a 'GVal' to an 'Integer'
-- The conversion will fail when the value is not an integer
toInteger :: GVal m -> Maybe Integer
toInteger :: GVal m -> Maybe Integer
toInteger = (Double -> Maybe Integer)
-> (Integer -> Maybe Integer)
-> Either Double Integer
-> Maybe Integer
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
Prelude.either (Maybe Integer -> Double -> Maybe Integer
forall a b. a -> b -> a
const Maybe Integer
forall a. Maybe a
Nothing) Integer -> Maybe Integer
forall a. a -> Maybe a
Just (Either Double Integer -> Maybe Integer)
-> (Scientific -> Either Double Integer)
-> Scientific
-> Maybe Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Scientific -> Either Double Integer
forall r i. (RealFloat r, Integral i) => Scientific -> Either r i
floatingOrInteger (Scientific -> Maybe Integer)
-> (GVal m -> Maybe Scientific) -> GVal m -> Maybe Integer
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber

-- | Convert a 'GVal' to an 'Int', falling back to the given
-- default if the conversion fails.
toIntDef :: Int -> GVal m -> Int
toIntDef :: Int -> GVal m -> Int
toIntDef Int
d = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
d (Maybe Int -> Int) -> (GVal m -> Maybe Int) -> GVal m -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Maybe Int
forall (m :: * -> *). GVal m -> Maybe Int
toInt

-- | Convert a 'GVal' to an 'Int', falling back to zero (0)
-- if the conversion fails.
toInt0 :: GVal m -> Int
toInt0 :: GVal m -> Int
toInt0 = Int -> GVal m -> Int
forall (m :: * -> *). Int -> GVal m -> Int
toIntDef Int
0

-- | Loose cast to boolean.
--
-- Numeric zero, empty strings, empty lists, empty objects, 'Null', and boolean
-- 'False' are considered falsy, anything else (including functions) is
-- considered true-ish.
toBoolean :: GVal m -> Bool
toBoolean :: GVal m -> Bool
toBoolean = GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
asBoolean

-- | Dynamically cast to a function.
-- This yields 'Just' a 'Function' if the value is a function, 'Nothing' if
-- it's not.
toFunction :: GVal m -> Maybe (Function m)
toFunction :: GVal m -> Maybe (Function m)
toFunction = GVal m -> Maybe (Function m)
forall (m :: * -> *). GVal m -> Maybe (Function m)
asFunction

picoToScientific :: Pico -> Scientific
picoToScientific :: Pico -> Scientific
picoToScientific (MkFixed Integer
x) = Integer -> Int -> Scientific
scientific Integer
x (-Int
12)

scientificToPico :: Scientific -> Pico
scientificToPico :: Scientific -> Pico
scientificToPico Scientific
s =
    Integer -> Pico
forall k (a :: k). Integer -> Fixed a
MkFixed (Scientific -> Integer
forall a b. (RealFrac a, Integral b) => a -> b
Prelude.floor (Scientific -> Integer) -> Scientific -> Integer
forall a b. (a -> b) -> a -> b
$ Integer -> Int -> Scientific
scientific (Scientific -> Integer
coefficient Scientific
s) (Scientific -> Int
base10Exponent Scientific
s Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
12))

{-#RULES "GVal/round-trip-Maybe" fromGVal . toGVal = Just #-}
{-#RULES "GVal/round-trip-Either" fromGValEither . toGVal = Right #-}
{-#RULES "GVal/text-shortcut" asText . toGVal = id #-}

class FromGVal m a where
    fromGValEither :: GVal m -> Either Prelude.String a
    fromGValEither = Either String a
-> (a -> Either String a) -> Maybe a -> Either String a
forall b a. b -> (a -> b) -> Maybe a -> b
Prelude.maybe (String -> Either String a
forall a b. a -> Either a b
Left String
"Conversion from GVal failed") a -> Either String a
forall a b. b -> Either a b
Right (Maybe a -> Either String a)
-> (GVal m -> Maybe a) -> GVal m -> Either String a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Maybe a
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal
    fromGVal :: GVal m -> Maybe a
    fromGVal = (String -> Maybe a) -> (a -> Maybe a) -> Either String a -> Maybe a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
Prelude.either (Maybe a -> String -> Maybe a
forall a b. a -> b -> a
const Maybe a
forall a. Maybe a
Nothing) a -> Maybe a
forall a. a -> Maybe a
Just (Either String a -> Maybe a)
-> (GVal m -> Either String a) -> GVal m -> Maybe a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Either String a
forall (m :: * -> *) a. FromGVal m a => GVal m -> Either String a
fromGValEither

fromGValM :: (MonadFail m, FromGVal m a) => GVal m -> m a
fromGValM :: GVal m -> m a
fromGValM = (String -> m a) -> (a -> m a) -> Either String a -> m a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
Prelude.either String -> m a
forall (m :: * -> *) a. MonadFail m => String -> m a
Prelude.fail a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String a -> m a)
-> (GVal m -> Either String a) -> GVal m -> m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Either String a
forall (m :: * -> *) a. FromGVal m a => GVal m -> Either String a
fromGValEither

instance FromGVal m Int where
    fromGVal :: GVal m -> Maybe Int
fromGVal = GVal m -> Maybe Int
forall (m :: * -> *). GVal m -> Maybe Int
toInt

instance FromGVal m Scientific where
    fromGVal :: GVal m -> Maybe Scientific
fromGVal = GVal m -> Maybe Scientific
forall (m :: * -> *). GVal m -> Maybe Scientific
asNumber

instance FromGVal m Integer where
    fromGVal :: GVal m -> Maybe Integer
fromGVal = GVal m -> Maybe Integer
forall (m :: * -> *). GVal m -> Maybe Integer
toInteger

instance FromGVal m Text where
    fromGVal :: GVal m -> Maybe Text
fromGVal = Text -> Maybe Text
forall a. a -> Maybe a
Just (Text -> Maybe Text) -> (GVal m -> Text) -> GVal m -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Text
forall (m :: * -> *). GVal m -> Text
asText

instance FromGVal m (GVal m) where
    fromGVal :: GVal m -> Maybe (GVal m)
fromGVal = GVal m -> Maybe (GVal m)
forall a. a -> Maybe a
Just

instance FromGVal m ByteString where
    fromGVal :: GVal m -> Maybe ByteString
fromGVal = GVal m -> Maybe ByteString
forall (m :: * -> *). GVal m -> Maybe ByteString
asBytes

instance FromGVal m LBS.ByteString where
    fromGVal :: GVal m -> Maybe ByteString
fromGVal = (ByteString -> ByteString) -> Maybe ByteString -> Maybe ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> ByteString
LBS.fromStrict (Maybe ByteString -> Maybe ByteString)
-> (GVal m -> Maybe ByteString) -> GVal m -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Maybe ByteString
forall (m :: * -> *). GVal m -> Maybe ByteString
asBytes

instance FromGVal m a => FromGVal m (Maybe a) where
    fromGVal :: GVal m -> Maybe (Maybe a)
fromGVal = \GVal m
g ->
        if GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isNull GVal m
g
            then
                Maybe a -> Maybe (Maybe a)
forall a. a -> Maybe a
Just Maybe a
forall a. Maybe a
Nothing
            else
                a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> Maybe a -> Maybe (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe a
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
g

instance FromGVal m Bool where
    fromGVal :: GVal m -> Maybe Bool
fromGVal = Bool -> Maybe Bool
forall a. a -> Maybe a
Just (Bool -> Maybe Bool) -> (GVal m -> Bool) -> GVal m -> Maybe Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
asBoolean

instance FromGVal m JSON.Value where
    fromGVal :: GVal m -> Maybe Value
fromGVal = GVal m -> Maybe Value
forall (m :: * -> *). GVal m -> Maybe Value
asJSON

instance FromGVal m () where
    fromGVal :: GVal m -> Maybe ()
fromGVal GVal m
g = if GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isNull GVal m
g then () -> Maybe ()
forall a. a -> Maybe a
Just () else Maybe ()
forall a. Maybe a
Nothing

instance FromGVal m a => FromGVal m [a] where
    fromGVal :: GVal m -> Maybe [a]
fromGVal GVal m
g = GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
g Maybe [GVal m] -> ([GVal m] -> Maybe [a]) -> Maybe [a]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (GVal m -> Maybe a) -> [GVal m] -> Maybe [a]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM GVal m -> Maybe a
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal

instance ( FromGVal m a
         , FromGVal m b
         ) => FromGVal m (a, b) where
    fromGVal :: GVal m -> Maybe (a, b)
fromGVal GVal m
g = case GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
g of
        Just [GVal m
a, GVal m
b] ->
            (,) (a -> b -> (a, b)) -> Maybe a -> Maybe (b -> (a, b))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe a
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
a
                Maybe (b -> (a, b)) -> Maybe b -> Maybe (a, b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GVal m -> Maybe b
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
b
        Maybe [GVal m]
_ -> Maybe (a, b)
forall a. Maybe a
Nothing

instance ( FromGVal m a
         , FromGVal m b
         , FromGVal m c
         ) => FromGVal m (a, b, c) where
    fromGVal :: GVal m -> Maybe (a, b, c)
fromGVal GVal m
g = case GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
g of
        Just [GVal m
a, GVal m
b, GVal m
c] ->
            (,,) (a -> b -> c -> (a, b, c))
-> Maybe a -> Maybe (b -> c -> (a, b, c))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe a
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
a
                 Maybe (b -> c -> (a, b, c)) -> Maybe b -> Maybe (c -> (a, b, c))
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GVal m -> Maybe b
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
b
                 Maybe (c -> (a, b, c)) -> Maybe c -> Maybe (a, b, c)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GVal m -> Maybe c
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
c
        Maybe [GVal m]
_ -> Maybe (a, b, c)
forall a. Maybe a
Nothing

instance ( FromGVal m a
         , FromGVal m b
         , FromGVal m c
         , FromGVal m d
         ) => FromGVal m (a, b, c, d) where
    fromGVal :: GVal m -> Maybe (a, b, c, d)
fromGVal GVal m
g = case GVal m -> Maybe [GVal m]
forall (m :: * -> *). GVal m -> Maybe [GVal m]
asList GVal m
g of
        Just [GVal m
a, GVal m
b, GVal m
c, GVal m
d] ->
            (,,,) (a -> b -> c -> d -> (a, b, c, d))
-> Maybe a -> Maybe (b -> c -> d -> (a, b, c, d))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m -> Maybe a
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
a
                  Maybe (b -> c -> d -> (a, b, c, d))
-> Maybe b -> Maybe (c -> d -> (a, b, c, d))
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GVal m -> Maybe b
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
b
                  Maybe (c -> d -> (a, b, c, d))
-> Maybe c -> Maybe (d -> (a, b, c, d))
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GVal m -> Maybe c
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
c
                  Maybe (d -> (a, b, c, d)) -> Maybe d -> Maybe (a, b, c, d)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GVal m -> Maybe d
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
d
        Maybe [GVal m]
_ -> Maybe (a, b, c, d)
forall a. Maybe a
Nothing

instance FromGVal m Day where
    fromGVal :: GVal m -> Maybe Day
fromGVal GVal m
g = do
        Integer
year <- Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Integer) -> Maybe Int -> Maybe Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (GVal m
g GVal m -> GVal m -> Maybe Int
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"year" :: Maybe Int)
        Int
month <- GVal m
g GVal m -> GVal m -> Maybe Int
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"month"
        Int
day <- GVal m
g GVal m -> GVal m -> Maybe Int
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"day"
        Day -> Maybe Day
forall (m :: * -> *) a. Monad m => a -> m a
return (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ Integer -> Int -> Int -> Day
fromGregorian Integer
year Int
month Int
day

instance FromGVal m TimeOfDay where
    fromGVal :: GVal m -> Maybe TimeOfDay
fromGVal GVal m
g = do
        Int
hours <- GVal m
g GVal m -> GVal m -> Maybe Int
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"hours"
        Int
minutes <- GVal m
g GVal m -> GVal m -> Maybe Int
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"minutes"
        Pico
seconds <- Scientific -> Pico
scientificToPico (Scientific -> Pico) -> Maybe Scientific -> Maybe Pico
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m
g GVal m -> GVal m -> Maybe Scientific
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"seconds"
        TimeOfDay -> Maybe TimeOfDay
forall (m :: * -> *) a. Monad m => a -> m a
return (TimeOfDay -> Maybe TimeOfDay) -> TimeOfDay -> Maybe TimeOfDay
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Pico -> TimeOfDay
TimeOfDay Int
hours Int
minutes Pico
seconds

instance FromGVal m LocalTime where
    fromGVal :: GVal m -> Maybe LocalTime
fromGVal GVal m
g = do
        Day
date <- GVal m -> Maybe Day
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
g Maybe Day -> Maybe Day -> Maybe Day
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> GVal m
g GVal m -> GVal m -> Maybe Day
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"date"
        TimeOfDay
time <- GVal m -> Maybe TimeOfDay
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
g Maybe TimeOfDay -> Maybe TimeOfDay -> Maybe TimeOfDay
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> GVal m
g GVal m -> GVal m -> Maybe TimeOfDay
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"time"
        LocalTime -> Maybe LocalTime
forall (m :: * -> *) a. Monad m => a -> m a
return (LocalTime -> Maybe LocalTime) -> LocalTime -> Maybe LocalTime
forall a b. (a -> b) -> a -> b
$ Day -> TimeOfDay -> LocalTime
LocalTime Day
date TimeOfDay
time

instance FromGVal m ZonedTime where
    fromGVal :: GVal m -> Maybe ZonedTime
fromGVal GVal m
g = do
        LocalTime
localTime <- GVal m -> Maybe LocalTime
forall (m :: * -> *) a. FromGVal m a => GVal m -> Maybe a
fromGVal GVal m
g
        TimeZone
timeZone <- GVal m
g GVal m -> GVal m -> Maybe TimeZone
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"tz"
        ZonedTime -> Maybe ZonedTime
forall (m :: * -> *) a. Monad m => a -> m a
return (ZonedTime -> Maybe ZonedTime) -> ZonedTime -> Maybe ZonedTime
forall a b. (a -> b) -> a -> b
$ LocalTime -> TimeZone -> ZonedTime
ZonedTime LocalTime
localTime TimeZone
timeZone

instance FromGVal m TimeZone where
    fromGVal :: GVal m -> Maybe TimeZone
fromGVal GVal m
g =
        Int -> Bool -> String -> TimeZone
TimeZone
            (Int -> Bool -> String -> TimeZone)
-> Maybe Int -> Maybe (Bool -> String -> TimeZone)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m
g GVal m -> GVal m -> Maybe Int
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"minutes"
            Maybe (Bool -> String -> TimeZone)
-> Maybe Bool -> Maybe (String -> TimeZone)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GVal m
g GVal m -> GVal m -> Maybe Bool
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"summerOnly"
            Maybe (String -> TimeZone) -> Maybe String -> Maybe TimeZone
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (Text -> String
Text.unpack (Text -> String) -> Maybe Text -> Maybe String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m
g GVal m -> GVal m -> Maybe Text
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"name")

instance FromGVal m TimeLocale where
    fromGVal :: GVal m -> Maybe TimeLocale
fromGVal GVal m
g =
        if GVal m -> Bool
forall (m :: * -> *). GVal m -> Bool
isDict GVal m
g
            then
                TimeLocale -> Maybe TimeLocale
forall a. a -> Maybe a
Just (TimeLocale -> Maybe TimeLocale) -> TimeLocale -> Maybe TimeLocale
forall a b. (a -> b) -> a -> b
$ [(String, String)]
-> [(String, String)]
-> (String, String)
-> String
-> String
-> String
-> String
-> [TimeZone]
-> TimeLocale
TimeLocale
                    ([(String, String)]
-> Maybe [(String, String)] -> [(String, String)]
forall a. a -> Maybe a -> a
fromMaybe (TimeLocale -> [(String, String)]
wDays TimeLocale
defaultTimeLocale) (Maybe [(String, String)] -> [(String, String)])
-> Maybe [(String, String)] -> [(String, String)]
forall a b. (a -> b) -> a -> b
$ ((Text, Text) -> (String, String))
-> [(Text, Text)] -> [(String, String)]
forall a b. (a -> b) -> [a] -> [b]
List.map (Text, Text) -> (String, String)
unpackPair ([(Text, Text)] -> [(String, String)])
-> Maybe [(Text, Text)] -> Maybe [(String, String)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m
g GVal m -> GVal m -> Maybe [(Text, Text)]
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"wDays")
                    ([(String, String)]
-> Maybe [(String, String)] -> [(String, String)]
forall a. a -> Maybe a -> a
fromMaybe (TimeLocale -> [(String, String)]
months TimeLocale
defaultTimeLocale) (Maybe [(String, String)] -> [(String, String)])
-> Maybe [(String, String)] -> [(String, String)]
forall a b. (a -> b) -> a -> b
$ ((Text, Text) -> (String, String))
-> [(Text, Text)] -> [(String, String)]
forall a b. (a -> b) -> [a] -> [b]
List.map (Text, Text) -> (String, String)
unpackPair ([(Text, Text)] -> [(String, String)])
-> Maybe [(Text, Text)] -> Maybe [(String, String)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m
g GVal m -> GVal m -> Maybe [(Text, Text)]
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"months")
                    ((String, String) -> Maybe (String, String) -> (String, String)
forall a. a -> Maybe a -> a
fromMaybe (TimeLocale -> (String, String)
amPm TimeLocale
defaultTimeLocale) (Maybe (String, String) -> (String, String))
-> Maybe (String, String) -> (String, String)
forall a b. (a -> b) -> a -> b
$ (Text, Text) -> (String, String)
unpackPair ((Text, Text) -> (String, String))
-> Maybe (Text, Text) -> Maybe (String, String)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m
g GVal m -> GVal m -> Maybe (Text, Text)
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"amPm")
                    (String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe (TimeLocale -> String
dateTimeFmt TimeLocale
defaultTimeLocale) (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ Text -> String
Text.unpack (Text -> String) -> Maybe Text -> Maybe String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m
g GVal m -> GVal m -> Maybe Text
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"dateTimeFmt")
                    (String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe (TimeLocale -> String
dateFmt TimeLocale
defaultTimeLocale) (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ Text -> String
Text.unpack (Text -> String) -> Maybe Text -> Maybe String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m
g GVal m -> GVal m -> Maybe Text
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"dateFmt")
                    (String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe (TimeLocale -> String
timeFmt TimeLocale
defaultTimeLocale) (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ Text -> String
Text.unpack (Text -> String) -> Maybe Text -> Maybe String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m
g GVal m -> GVal m -> Maybe Text
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"timeFmt")
                    (String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe (TimeLocale -> String
time12Fmt TimeLocale
defaultTimeLocale) (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ Text -> String
Text.unpack (Text -> String) -> Maybe Text -> Maybe String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GVal m
g GVal m -> GVal m -> Maybe Text
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"time12Fmt")
                    ([TimeZone] -> Maybe [TimeZone] -> [TimeZone]
forall a. a -> Maybe a -> a
fromMaybe (TimeLocale -> [TimeZone]
knownTimeZones TimeLocale
defaultTimeLocale) (Maybe [TimeZone] -> [TimeZone]) -> Maybe [TimeZone] -> [TimeZone]
forall a b. (a -> b) -> a -> b
$ GVal m
g GVal m -> GVal m -> Maybe [TimeZone]
forall (m :: * -> *) v. FromGVal m v => GVal m -> GVal m -> Maybe v
~! GVal m
"knownTimeZones")
            else
                Maybe TimeLocale
forall a. Maybe a
Nothing

pairwise :: (a -> b) -> (a, a) -> (b, b)
pairwise :: (a -> b) -> (a, a) -> (b, b)
pairwise a -> b
f (a
a, a
b) = (a -> b
f a
a, a -> b
f a
b)

packPair :: ([Char], [Char]) -> (Text, Text)
packPair :: (String, String) -> (Text, Text)
packPair = (String -> Text) -> (String, String) -> (Text, Text)
forall a b. (a -> b) -> (a, a) -> (b, b)
pairwise String -> Text
Text.pack

unpackPair :: (Text, Text) -> ([Char], [Char])
unpackPair :: (Text, Text) -> (String, String)
unpackPair = (Text -> String) -> (Text, Text) -> (String, String)
forall a b. (a -> b) -> (a, a) -> (b, b)
pairwise Text -> String
Text.unpack