svfactor-0.1: src/Data/Svfactor/Syntax/Field.hs
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveGeneric #-}
{-|
Module : Data.Svfactor.Syntax.Field
Copyright : (C) CSIRO 2017-2018
License : BSD3
Maintainer : George Wilson <george.wilson@data61.csiro.au>
Stability : experimental
Portability : non-portable
-}
module Data.Svfactor.Syntax.Field (
Field (Unquoted, Quoted)
, SpacedField
, Spaced (Spaced)
, HasFields (fields)
, AsField (_Field, _Unquoted, _Quoted)
, unescapedField
, foldField
, fieldContents
) where
import Control.Applicative ((<$>))
import Control.DeepSeq (NFData)
import Control.Lens (Lens, Prism', Traversal, lens, prism)
import Data.Foldable (Foldable (foldMap))
import Data.Functor (Functor (fmap))
import Data.Traversable (Traversable (traverse))
import GHC.Generics (Generic)
import Data.Svfactor.Text.Escape (Unescaped (Unescaped, getRawUnescaped))
import Data.Svfactor.Text.Quote (Quote)
import Data.Svfactor.Text.Space (Spaced (Spaced))
-- | A 'Field' is a single cell from a CSV document.
--
-- Its value is either 'Quoted', which indicates the type of quote
-- surrounding the value, or it is 'Unquoted', containing only the value.
data Field s =
Unquoted s
| Quoted Quote (Unescaped s)
deriving (Eq, Ord, Show, Generic)
instance NFData s => NFData (Field s)
instance Functor Field where
fmap f fi = case fi of
Unquoted s -> Unquoted (f s)
Quoted q v -> Quoted q (fmap f v)
instance Foldable Field where
foldMap f fi = case fi of
Unquoted s -> f s
Quoted _ v -> foldMap f v
instance Traversable Field where
traverse f fi = case fi of
Unquoted s -> Unquoted <$> f s
Quoted q v -> Quoted q <$> traverse f v
-- | 'Field's are often surrounded by spaces
type SpacedField a = Spaced (Field a)
-- | Classy prisms for 'Field'
class (HasFields s s a a) => AsField s a | s -> a where
_Field :: Prism' s (Field a)
_Unquoted :: Prism' s a
_Quoted :: Prism' s (Quote, Unescaped a)
_Unquoted = _Field . _Unquoted
{-# INLINE _Unquoted #-}
_Quoted = _Field . _Quoted
{-# INLINE _Quoted #-}
instance AsField (Field a) a where
_Field = id
{-# INLINE _Field #-}
_Unquoted = prism Unquoted
(\x -> case x of
Unquoted y -> Right y
_ -> Left x
)
{-# INLINE _Unquoted #-}
_Quoted = prism (uncurry Quoted)
(\x -> case x of
Quoted y z -> Right (y,z)
_ -> Left x
)
{-# INLINE _Quoted #-}
-- | Classy 'Traversal' for things containing 'Field's
class HasFields c d s t | c -> s, d -> t, c t -> d, d s -> c where
fields :: Traversal c d (Field s) (Field t)
instance HasFields (Field s) (Field t) s t where
fields = id
{-# INLINE fields #-}
-- | Build a quoted field with a normal string
unescapedField :: Quote -> s -> Field s
unescapedField q s = Quoted q (Unescaped s)
-- | The catamorphism for @Field'@
foldField :: (s -> b) -> (Quote -> Unescaped s -> b) -> Field s -> b
foldField u q fi = case fi of
Unquoted s -> u s
Quoted a b -> q a b
-- | Lens into the contents of a Field, regardless of whether it's quoted or unquoted
fieldContents :: Lens (Field s) (Field t) s t
fieldContents =
lens (foldField id (const getRawUnescaped)) $ \f b -> case f of
Unquoted _ -> Unquoted b
Quoted q _ -> Quoted q (Unescaped b)