module Conduit.Utils where

import Data.Aeson (FromJSON(..), Key, ToJSON(..), Value, object, withObject, (.=))
import Data.Aeson.KeyMap (toAscList)
import Data.Aeson.Types (Parser)

-- | A flipped (.) for L->R function composition.
(.-) :: (a -> b) -> (b -> c) -> (a -> c)
a -> b
f .- :: forall a b c. (a -> b) -> (b -> c) -> a -> c
.- b -> c
g = b -> c
g (b -> c) -> (a -> b) -> a -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> b
f
{-# INLINE (.-) #-}

-- | Like 'uncurry', but for ternary functions.
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 :: forall a b c d. (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 a -> b -> c -> d
fn (a
a, b
b, c
c) = a -> b -> c -> d
fn a
a b
b c
c
{-# INLINE uncurry3 #-}

-- | Like L->R Kleisli composition, but ignores result of the first operation.
--
-- > panic :: Text -> IO a
-- > -- panic msg = putTextLn msg >>= const exitFailure
-- > panic = putTextLn >-> exitFailure
infixl 5 >->
(>->) :: (Monad m) => (a -> m b) -> m c -> a -> m c
a -> m b
a >-> :: forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> m c -> a -> m c
>-> m c
b = a -> m b
a (a -> m b) -> (b -> m c) -> a -> m c
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> m c -> b -> m c
forall a b. a -> b -> a
const m c
b
{-# INLINE (>->) #-}

-- | An easy method of generating a containing JSON object as the API Spec [often requires](https://realworld-docs.netlify.app/docs/specs/backend-specs/api-response-format).
-- 
-- > inObj "key" 123
--  
--   generates
-- 
-- > { "key": 123 }
data InObj obj = InObj
  { forall obj. InObj obj -> Key
objName   :: !Key
  , forall obj. InObj obj -> obj
objItself :: !obj
  } deriving (Int -> InObj obj -> ShowS
[InObj obj] -> ShowS
InObj obj -> String
(Int -> InObj obj -> ShowS)
-> (InObj obj -> String)
-> ([InObj obj] -> ShowS)
-> Show (InObj obj)
forall obj. Show obj => Int -> InObj obj -> ShowS
forall obj. Show obj => [InObj obj] -> ShowS
forall obj. Show obj => InObj obj -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall obj. Show obj => Int -> InObj obj -> ShowS
showsPrec :: Int -> InObj obj -> ShowS
$cshow :: forall obj. Show obj => InObj obj -> String
show :: InObj obj -> String
$cshowList :: forall obj. Show obj => [InObj obj] -> ShowS
showList :: [InObj obj] -> ShowS
Show)

instance (ToJSON obj) => ToJSON (InObj obj) where
  toJSON :: InObj obj -> Value
  toJSON :: InObj obj -> Value
toJSON InObj {obj
Key
$sel:objName:InObj :: forall obj. InObj obj -> Key
$sel:objItself:InObj :: forall obj. InObj obj -> obj
objName :: Key
objItself :: obj
..} =
    [Pair] -> Value
object [Key
objName Key -> obj -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= obj
objItself]

instance (FromJSON obj) => FromJSON (InObj obj) where
  parseJSON :: Value -> Parser (InObj obj)
  parseJSON :: Value -> Parser (InObj obj)
parseJSON = String
-> (Object -> Parser (InObj obj)) -> Value -> Parser (InObj obj)
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"InObj" ((Object -> Parser (InObj obj)) -> Value -> Parser (InObj obj))
-> (Object -> Parser (InObj obj)) -> Value -> Parser (InObj obj)
forall a b. (a -> b) -> a -> b
$ \Object
v -> do
    case Object -> [Pair]
forall v. KeyMap v -> [(Key, v)]
toAscList Object
v of
      [(Key
key, Value
value)] -> do
        obj
obj <- Value -> Parser obj
forall a. FromJSON a => Value -> Parser a
parseJSON Value
value
        InObj obj -> Parser (InObj obj)
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (InObj obj -> Parser (InObj obj))
-> InObj obj -> Parser (InObj obj)
forall a b. (a -> b) -> a -> b
$ Key -> obj -> InObj obj
forall obj. Key -> obj -> InObj obj
InObj Key
key obj
obj
      [Pair]
_ -> String -> Parser (InObj obj)
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Expected a single key-value pair"