packages feed

argo-0.2022.2.2: source/library/Argo/Codec/Codec.hs

{-# LANGUAGE FlexibleInstances #-}

module Argo.Codec.Codec where

import Control.Applicative ((<|>))

import qualified Control.Applicative as Applicative

data Codec r w s i o = Codec
    { decode :: r o
    , encode :: i -> w o
    , schema :: s
    }

instance (Functor r, Functor w) => Functor (Codec r w s i) where
    fmap f c = Codec
        { decode = f <$> decode c
        , encode = fmap f . encode c
        , schema = schema c
        }

instance
    ( Applicative r
    , Applicative w
    , Applicative s
    , Monoid a
    ) => Applicative (Codec r w (s a) i) where
    pure x = Codec
        { decode = pure x
        , encode = const $ pure x
        , schema = pure mempty
        }
    cf <*> cx = Codec
        { decode = decode cf <*> decode cx
        , encode = \i -> encode cf i <*> encode cx i
        , schema = (<>) <$> schema cf <*> schema cx
        }

instance
    ( Applicative.Alternative r
    , Applicative.Alternative w
    , Applicative s
    , Monoid a
    ) => Applicative.Alternative (Codec r w (s a) i) where
    empty = Codec
        { decode = Applicative.empty
        , encode = const Applicative.empty
        , schema = pure mempty
        }
    cx <|> cy = Codec
        { decode = decode cx <|> decode cy
        , encode = \i -> encode cx i <|> encode cy i
        , schema = (<>) <$> schema cx <*> schema cy
        }

map
    :: (Functor r, Functor w)
    => (a -> b)
    -> (b -> a)
    -> Codec r w s a a
    -> Codec r w s b b
map f g c = Codec
    { decode = f <$> decode c
    , encode = fmap f . encode c . g
    , schema = schema c
    }

mapMaybe
    :: (Applicative.Alternative r, Applicative.Alternative w, Monad r, Monad w)
    => (o2 -> Maybe o1)
    -> (i1 -> Maybe i2)
    -> Codec r w s i2 o2
    -> Codec r w s i1 o1
mapMaybe f g c = Codec
    { decode = do
        o2 <- decode c
        toAlternative $ f o2
    , encode = \i1 -> do
        i2 <- toAlternative $ g i1
        o2 <- encode c i2
        toAlternative $ f o2
    , schema = schema c
    }

project :: (i -> f) -> Codec r w s f o -> Codec r w s i o
project f c = c { encode = encode c . f }

tap :: Functor f => (a -> f b) -> a -> f a
tap f x = x <$ f x

toAlternative :: Applicative.Alternative m => Maybe a -> m a
toAlternative = maybe Applicative.empty pure