83 lines
3.3 KiB
Haskell
83 lines
3.3 KiB
Haskell
{-# LANGUAGE RankNTypes #-}
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
module Colonnade.Decoding where
|
|
|
|
import Colonnade.Internal (EitherWrap(..))
|
|
import Colonnade.Types
|
|
import Data.Functor.Contravariant
|
|
import Data.Vector (Vector)
|
|
import qualified Data.Vector as Vector
|
|
|
|
-- | Converts the content type of a 'Decoding'. The @'Contravariant' f@
|
|
-- constraint means that @f@ can be 'Headless' but not 'Headed'.
|
|
contramapContent :: forall c1 c2 f a. Contravariant f => (c2 -> c1) -> Decoding f c1 a -> Decoding f c2 a
|
|
contramapContent f = go
|
|
where
|
|
go :: forall b. Decoding f c1 b -> Decoding f c2 b
|
|
go (DecodingPure x) = DecodingPure x
|
|
go (DecodingAp h decode apNext) =
|
|
DecodingAp (contramap f h) (decode . f) (go apNext)
|
|
|
|
headless :: (content -> Either String a) -> Decoding Headless content a
|
|
headless f = DecodingAp Headless f (DecodingPure id)
|
|
|
|
headed :: content -> (content -> Either String a) -> Decoding Headed content a
|
|
headed h f = DecodingAp (Headed h) f (DecodingPure id)
|
|
|
|
uncheckedRun :: forall content a.
|
|
Vector content
|
|
-> Decoding Indexed content a
|
|
-> Either (DecodingErrors Indexed content) a
|
|
uncheckedRun v = getEitherWrap . go
|
|
where
|
|
go :: forall b.
|
|
Decoding Indexed content b
|
|
-> EitherWrap (DecodingErrors Indexed content) b
|
|
go (DecodingPure b) = EitherWrap (Right b)
|
|
go (DecodingAp (Indexed ix) decode apNext) =
|
|
let rnext = go apNext
|
|
content = Vector.unsafeIndex v ix
|
|
rcurrent = mapLeft (DecodingErrors . Vector.singleton . DecodingError content (Indexed ix)) (decode content)
|
|
in rnext <*> (EitherWrap rcurrent)
|
|
|
|
-- | Maps over a 'Decoding' that expects headers, converting these
|
|
-- expected headers into the indices of the columns that they
|
|
-- correspond to.
|
|
headedToIndexed :: forall content a. Eq content
|
|
=> Vector content -- ^ Headers in the source document
|
|
-> Decoding Headed content a -- ^ Decoding that contains expected headers
|
|
-> Either (HeadingErrors content) (Decoding Indexed content a)
|
|
headedToIndexed v = go
|
|
where
|
|
go :: forall b. Eq content
|
|
=> Decoding Headed content b
|
|
-> Either (HeadingErrors content) (Decoding Indexed content b)
|
|
go (DecodingPure b) = Right (DecodingPure b)
|
|
go (DecodingAp (Headed h) decode apNext) =
|
|
let rnext = go apNext
|
|
ixs = Vector.elemIndices h v
|
|
ixsLen = Vector.length ixs
|
|
rcurrent
|
|
| ixsLen == 1 = Right (Vector.unsafeIndex ixs 0)
|
|
| ixsLen == 0 = Left (HeadingErrors (Vector.singleton h) Vector.empty)
|
|
| otherwise = Left (HeadingErrors Vector.empty (Vector.singleton (h,ixsLen)))
|
|
in case rcurrent of
|
|
Right ix -> case rnext of
|
|
Right apIx -> Right (DecodingAp (Indexed ix) decode apIx)
|
|
Left errNext -> Left errNext
|
|
Left err -> case rnext of
|
|
Right _ -> Left err
|
|
Left errNext -> Left (mappend err errNext)
|
|
|
|
eitherMonoidAp :: Monoid a => Either a (b -> c) -> Either a b -> Either a c
|
|
eitherMonoidAp = go where
|
|
go (Left a1) (Left a2) = Left (mappend a1 a2)
|
|
go (Left a1) (Right _) = Left a1
|
|
go (Right _) (Left a2) = Left a2
|
|
go (Right f) (Right b) = Right (f b)
|
|
|
|
mapLeft :: (a -> b) -> Either a c -> Either b c
|
|
mapLeft _ (Right a) = Right a
|
|
mapLeft f (Left a) = Left (f a)
|
|
|