packages feed

siphon-0.8.2.1: src/Siphon/Types.hs

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Siphon.Types
  ( Siphon (..)
  , Indexed (..)
  , SiphonError (..)
  , RowError (..)
  , CellError (..)
  ) where

import Control.Exception (Exception)
import Data.Functor.Classes (Eq1, Show1, liftEq, liftShowsPrec)
import Data.Text (Text)
import Data.Vector (Vector)

data CellError = CellError
  { cellErrorColumn :: !Int
  , cellErrorContent :: !Text
  }
  deriving (Show, Read, Eq)

newtype Indexed a = Indexed
  { indexedIndex :: Int
  }
  deriving (Eq, Ord, Functor, Show, Read)

instance Show1 Indexed where
  liftShowsPrec _ _ p (Indexed i) s = showsPrec p i s

instance Eq1 Indexed where
  liftEq _ (Indexed i) (Indexed j) = i == j

data SiphonError = SiphonError
  { siphonErrorRow :: !Int
  , siphonErrorCause :: !RowError
  }
  deriving (Show, Read, Eq)

instance Exception SiphonError

data RowError
  = -- | Error occurred parsing the document into cells
    RowErrorParse
  | -- | Error decoding the content
    RowErrorDecode !(Vector CellError)
  | -- | Wrong number of cells in the row
    RowErrorSize !Int !Int
  | -- | Three parts:
    --   (a) Multiple header cells matched the same expected cell,
    --   (b) Headers that were missing,
    --   (c) Missing headers that were lambdas. They cannot be
    --   shown so instead their positions in the 'Siphon' are given.
    RowErrorHeaders !(Vector (Vector CellError)) !(Vector Text) !(Vector Int)
  | -- | Not enough cells in header, expected, actual
    RowErrorHeaderSize !Int !Int
  | -- | Error decoding unicode content, column number
    RowErrorMalformed !Int
  deriving (Show, Read, Eq)

{- | This just actually a specialization of the free applicative.
  Check out @Control.Applicative.Free@ in the @free@ library to
  learn more about this. The meanings of the fields are documented
  slightly more in the source code. Unfortunately, haddock does not
  play nicely with GADTs.
-}
data Siphon f c a where
  SiphonPure ::
    !a -> -- function
    Siphon f c a
  SiphonAp ::
    !(f c) -> -- header
    !(c -> Maybe a) -> -- decoding function
    !(Siphon f c (a -> b)) -> -- next decoding
    Siphon f c b

instance Functor (Siphon f c) where
  fmap f (SiphonPure a) = SiphonPure (f a)
  fmap f (SiphonAp h c apNext) = SiphonAp h c ((f .) <$> apNext)

instance Applicative (Siphon f c) where
  pure = SiphonPure
  SiphonPure f <*> y = fmap f y
  SiphonAp h c y <*> z = SiphonAp h c (flip <$> y <*> z)