{-# LANGUAGE DeriveGeneric       #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell     #-}
-- the following ones are necessary for the generics-sop magic
{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE FlexibleInstances   #-}
{-# LANGUAGE TypeOperators       #-}

module Poseidon.ColumnTypesUtils where

import           Data.ByteString       as S
import qualified Data.ByteString.Char8 as Bchs
import           Data.Char             (chr, ord)
import qualified Data.Csv              as Csv
import qualified Data.HashMap.Strict   as HM
import qualified Data.List             as L
import qualified Data.Text             as T
import qualified Data.Text.Encoding    as T
import           Data.Typeable         (Typeable)
import           Generics.SOP          (All, Generic (Code, from),
                                        HCollapse (hcollapse), I (..), K (K),
                                        Proxy (..), hcmap, unSOP, unZ)
import           GHC.Generics          as G hiding (conName)
import           Language.Haskell.TH   (Con (..), Dec (..), DecsQ, Info (..),
                                        Name, conE, conP, conT, mkName, reify,
                                        varE, varP)
import qualified Text.Parsec           as P
import qualified Text.Parsec.String    as P

-- a typeclass for types with smart constructors
class Makeable a where
    make :: MonadFail m => T.Text -> m a

-- a typeclass for .csv/.tsv column types that may require a logged warning when read
class Suspicious a where
    inspect :: a -> Maybe [String]

instance Suspicious String where
    inspect :: [Char] -> Maybe [[Char]]
inspect [Char]
_ = Maybe [[Char]]
forall a. Maybe a
Nothing
instance Suspicious a => Suspicious (Maybe a) where
    inspect :: Maybe a -> Maybe [[Char]]
inspect Maybe a
Nothing  = Maybe [[Char]]
forall a. Maybe a
Nothing
    inspect (Just a
x) = a -> Maybe [[Char]]
forall a. Suspicious a => a -> Maybe [[Char]]
inspect a
x
instance Suspicious a => Suspicious (ListColumn a) where
    inspect :: ListColumn a -> Maybe [[Char]]
inspect (ListColumn [a]
xs) = [[[Char]]] -> [[Char]]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
L.concat ([[[Char]]] -> [[Char]]) -> Maybe [[[Char]]] -> Maybe [[Char]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (a -> Maybe [[Char]]) -> [a] -> Maybe [[[Char]]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM a -> Maybe [[Char]]
forall a. Suspicious a => a -> Maybe [[Char]]
inspect [a]
xs
instance Suspicious CsvNamedRecord where
    inspect :: CsvNamedRecord -> Maybe [[Char]]
inspect CsvNamedRecord
_ =  Maybe [[Char]]
forall a. Maybe a
Nothing

-- generics-sop magic to inspect all fields of a JannoRow or SeqSourceRow with one command
inspectEachField :: (Generics.SOP.Generic a, Code a ~ '[ xs ], All Suspicious xs) => a -> [Maybe [String]] --'
inspectEachField :: forall a (xs :: [*]).
(Generic a, Code a ~ '[xs], All Suspicious xs) =>
a -> [Maybe [[Char]]]
inspectEachField =
      NP (K (Maybe [[Char]])) xs -> [Maybe [[Char]]]
NP (K (Maybe [[Char]])) xs -> CollapseTo NP (Maybe [[Char]])
forall (xs :: [*]) a.
SListIN NP xs =>
NP (K a) xs -> CollapseTo NP a
forall k l (h :: (k -> *) -> l -> *) (xs :: l) a.
(HCollapse h, SListIN h xs) =>
h (K a) xs -> CollapseTo h a
hcollapse
    (NP (K (Maybe [[Char]])) xs -> [Maybe [[Char]]])
-> (a -> NP (K (Maybe [[Char]])) xs) -> a -> [Maybe [[Char]]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Proxy Suspicious
-> (forall a. Suspicious a => I a -> K (Maybe [[Char]]) a)
-> NP I xs
-> NP (K (Maybe [[Char]])) xs
forall {k} {l} (h :: (k -> *) -> l -> *) (c :: k -> Constraint)
       (xs :: l) (proxy :: (k -> Constraint) -> *) (f :: k -> *)
       (f' :: k -> *).
(AllN (Prod h) c xs, HAp h) =>
proxy c
-> (forall (a :: k). c a => f a -> f' a) -> h f xs -> h f' xs
hcmap (Proxy Suspicious
forall {k} (t :: k). Proxy t
Proxy :: Proxy Suspicious) (\(I a
x) -> Maybe [[Char]] -> K (Maybe [[Char]]) a
forall k a (b :: k). a -> K a b
K (Maybe [[Char]] -> K (Maybe [[Char]]) a)
-> Maybe [[Char]] -> K (Maybe [[Char]]) a
forall a b. (a -> b) -> a -> b
$ a -> Maybe [[Char]]
forall a. Suspicious a => a -> Maybe [[Char]]
inspect a
x)
    (NP I xs -> NP (K (Maybe [[Char]])) xs)
-> (a -> NP I xs) -> a -> NP (K (Maybe [[Char]])) xs
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NS (NP I) '[xs] -> NP I xs
forall {k} (f :: k -> *) (x :: k). NS f '[x] -> f x
unZ (NS (NP I) '[xs] -> NP I xs)
-> (a -> NS (NP I) '[xs]) -> a -> NP I xs
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SOP I '[xs] -> NS (NP I) '[xs]
forall {k} (f :: k -> *) (xss :: [[k]]). SOP f xss -> NS (NP f) xss
unSOP (SOP I '[xs] -> NS (NP I) '[xs])
-> (a -> SOP I '[xs]) -> a -> NS (NP I) '[xs]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> SOP I '[xs]
a -> Rep a
forall a. Generic a => a -> Rep a
Generics.SOP.from

-- helper functions
parseTypeCSV :: forall a m. (MonadFail m, Makeable a, Typeable a) => String -> S.ByteString -> m a
parseTypeCSV :: forall a (m :: * -> *).
(MonadFail m, Makeable a, Typeable a) =>
[Char] -> ByteString -> m a
parseTypeCSV [Char]
colname ByteString
x = case ByteString -> Either UnicodeException Text
T.decodeUtf8' ByteString
x of
        Left UnicodeException
e  -> [Char] -> m a
forall a. [Char] -> m a
forall (m :: * -> *) a. MonadFail m => [Char] -> m a
fail ([Char] -> m a) -> [Char] -> m a
forall a b. (a -> b) -> a -> b
$ UnicodeException -> [Char]
forall a. Show a => a -> [Char]
show UnicodeException
e [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" in column " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
colname
        Right Text
t -> Text -> m a
forall a (m :: * -> *). (Makeable a, MonadFail m) => Text -> m a
forall (m :: * -> *). MonadFail m => Text -> m a
make (Text -> m a) -> Text -> m a
forall a b. (a -> b) -> a -> b
$ Text -> Text
T.strip Text
t

-- template haskell function to generate repetitive instances
makeInstances :: Name -> String -> DecsQ
makeInstances :: Name -> [Char] -> DecsQ
makeInstances Name
name [Char]
col = do
    TyConI (NewtypeD Cxt
_ Name
_ [TyVarBndr ()]
_ Maybe Kind
_ (NormalC Name
conName [BangType]
_) [DerivClause]
_) <- Name -> Q Info
reify Name
name
    let x :: Name
x = [Char] -> Name
mkName [Char]
"x"
    [d|
      instance Makeable $(Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT Name
name) where      make txt = return $ $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
conE Name
conName) txt
      instance Suspicious $(Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT Name
name) where    inspect _ = Nothing
      instance Show $(Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT Name
name) where          show $(Name -> [Q Pat] -> Q Pat
forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP Name
conName [Name -> Q Pat
forall (m :: * -> *). Quote m => Name -> m Pat
varP Name
x]) = T.unpack $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
varE Name
x)
      instance Csv.ToField $(Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT Name
name) where   toField $(Name -> [Q Pat] -> Q Pat
forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP Name
conName [Name -> Q Pat
forall (m :: * -> *). Quote m => Name -> m Pat
varP Name
x]) = Csv.toField $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
varE Name
x)
      instance Csv.FromField $(Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT Name
name) where parseField = parseTypeCSV col
      |]

-- general encoding/decoding options
decodingOptions :: Csv.DecodeOptions
decodingOptions :: DecodeOptions
decodingOptions = DecodeOptions
Csv.defaultDecodeOptions {
    Csv.decDelimiter = fromIntegral (ord '\t')
}
encodingOptions :: Csv.EncodeOptions
encodingOptions :: EncodeOptions
encodingOptions = EncodeOptions
Csv.defaultEncodeOptions {
      Csv.encDelimiter = fromIntegral (ord '\t')
    , Csv.encUseCrLf = False
    , Csv.encIncludeHeader = True
    , Csv.encQuoting = Csv.QuoteMinimal
}

-- | A datatype to collect additional, unpecified .csv/.tsv file columns (a hashmap in cassava/Data.Csv)
newtype CsvNamedRecord = CsvNamedRecord Csv.NamedRecord deriving (Int -> CsvNamedRecord -> [Char] -> [Char]
[CsvNamedRecord] -> [Char] -> [Char]
CsvNamedRecord -> [Char]
(Int -> CsvNamedRecord -> [Char] -> [Char])
-> (CsvNamedRecord -> [Char])
-> ([CsvNamedRecord] -> [Char] -> [Char])
-> Show CsvNamedRecord
forall a.
(Int -> a -> [Char] -> [Char])
-> (a -> [Char]) -> ([a] -> [Char] -> [Char]) -> Show a
$cshowsPrec :: Int -> CsvNamedRecord -> [Char] -> [Char]
showsPrec :: Int -> CsvNamedRecord -> [Char] -> [Char]
$cshow :: CsvNamedRecord -> [Char]
show :: CsvNamedRecord -> [Char]
$cshowList :: [CsvNamedRecord] -> [Char] -> [Char]
showList :: [CsvNamedRecord] -> [Char] -> [Char]
Show, CsvNamedRecord -> CsvNamedRecord -> Bool
(CsvNamedRecord -> CsvNamedRecord -> Bool)
-> (CsvNamedRecord -> CsvNamedRecord -> Bool) -> Eq CsvNamedRecord
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CsvNamedRecord -> CsvNamedRecord -> Bool
== :: CsvNamedRecord -> CsvNamedRecord -> Bool
$c/= :: CsvNamedRecord -> CsvNamedRecord -> Bool
/= :: CsvNamedRecord -> CsvNamedRecord -> Bool
Eq, (forall x. CsvNamedRecord -> Rep CsvNamedRecord x)
-> (forall x. Rep CsvNamedRecord x -> CsvNamedRecord)
-> Generic CsvNamedRecord
forall x. Rep CsvNamedRecord x -> CsvNamedRecord
forall x. CsvNamedRecord -> Rep CsvNamedRecord x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. CsvNamedRecord -> Rep CsvNamedRecord x
from :: forall x. CsvNamedRecord -> Rep CsvNamedRecord x
$cto :: forall x. Rep CsvNamedRecord x -> CsvNamedRecord
to :: forall x. Rep CsvNamedRecord x -> CsvNamedRecord
G.Generic)

getCsvNR :: CsvNamedRecord -> Csv.NamedRecord
getCsvNR :: CsvNamedRecord -> NamedRecord
getCsvNR (CsvNamedRecord NamedRecord
x) = NamedRecord
x

-- helper functions for .csv/.tsv reading
filterLookup :: Csv.FromField a => Csv.NamedRecord -> Bchs.ByteString -> Csv.Parser a
filterLookup :: forall a. FromField a => NamedRecord -> ByteString -> Parser a
filterLookup NamedRecord
m ByteString
name = case Maybe ByteString -> Maybe ByteString
cleanInput (Maybe ByteString -> Maybe ByteString)
-> Maybe ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> NamedRecord -> Maybe ByteString
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup ByteString
name NamedRecord
m of
    Maybe ByteString
Nothing -> [Char] -> Parser a
forall a. [Char] -> Parser a
forall (m :: * -> *) a. MonadFail m => [Char] -> m a
fail [Char]
"Missing value in mandatory column"
    Just ByteString
x  -> ByteString -> Parser a
forall a. FromField a => ByteString -> Parser a
Csv.parseField  ByteString
x

filterLookupOptional :: Csv.FromField a => Csv.NamedRecord -> Bchs.ByteString -> Csv.Parser (Maybe a)
filterLookupOptional :: forall a.
FromField a =>
NamedRecord -> ByteString -> Parser (Maybe a)
filterLookupOptional NamedRecord
m ByteString
name = Parser (Maybe a)
-> (ByteString -> Parser (Maybe a))
-> Maybe ByteString
-> Parser (Maybe a)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Maybe a -> Parser (Maybe a)
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing) ByteString -> Parser (Maybe a)
forall a. FromField a => ByteString -> Parser a
Csv.parseField (Maybe ByteString -> Parser (Maybe a))
-> (Maybe ByteString -> Maybe ByteString)
-> Maybe ByteString
-> Parser (Maybe a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe ByteString -> Maybe ByteString
cleanInput (Maybe ByteString -> Parser (Maybe a))
-> Maybe ByteString -> Parser (Maybe a)
forall a b. (a -> b) -> a -> b
$ ByteString -> NamedRecord -> Maybe ByteString
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup ByteString
name NamedRecord
m

cleanInput :: Maybe Bchs.ByteString -> Maybe Bchs.ByteString
cleanInput :: Maybe ByteString -> Maybe ByteString
cleanInput Maybe ByteString
Nothing           = Maybe ByteString
forall a. Maybe a
Nothing
cleanInput (Just ByteString
rawInputBS) = ByteString -> Maybe ByteString
transNA ByteString
rawInputBS
    where
        transNA :: Bchs.ByteString -> Maybe Bchs.ByteString
        transNA :: ByteString -> Maybe ByteString
transNA ByteString
""    = Maybe ByteString
forall a. Maybe a
Nothing
        transNA ByteString
"n/a" = Maybe ByteString
forall a. Maybe a
Nothing
        transNA ByteString
x     = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
x

explicitNA :: Csv.NamedRecord -> Csv.NamedRecord
explicitNA :: NamedRecord -> NamedRecord
explicitNA = (ByteString -> ByteString) -> NamedRecord -> NamedRecord
forall v1 v2 k. (v1 -> v2) -> HashMap k v1 -> HashMap k v2
HM.map (\ByteString
x -> if ByteString -> Bool
Bchs.null ByteString
x then ByteString
"n/a" else ByteString
x)

-- | A general datatype for janno list columns
newtype ListColumn a = ListColumn {forall a. ListColumn a -> [a]
getListColumn :: [a]}
    deriving (ListColumn a -> ListColumn a -> Bool
(ListColumn a -> ListColumn a -> Bool)
-> (ListColumn a -> ListColumn a -> Bool) -> Eq (ListColumn a)
forall a. Eq a => ListColumn a -> ListColumn a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall a. Eq a => ListColumn a -> ListColumn a -> Bool
== :: ListColumn a -> ListColumn a -> Bool
$c/= :: forall a. Eq a => ListColumn a -> ListColumn a -> Bool
/= :: ListColumn a -> ListColumn a -> Bool
Eq, Eq (ListColumn a)
Eq (ListColumn a) =>
(ListColumn a -> ListColumn a -> Ordering)
-> (ListColumn a -> ListColumn a -> Bool)
-> (ListColumn a -> ListColumn a -> Bool)
-> (ListColumn a -> ListColumn a -> Bool)
-> (ListColumn a -> ListColumn a -> Bool)
-> (ListColumn a -> ListColumn a -> ListColumn a)
-> (ListColumn a -> ListColumn a -> ListColumn a)
-> Ord (ListColumn a)
ListColumn a -> ListColumn a -> Bool
ListColumn a -> ListColumn a -> Ordering
ListColumn a -> ListColumn a -> ListColumn a
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall a. Ord a => Eq (ListColumn a)
forall a. Ord a => ListColumn a -> ListColumn a -> Bool
forall a. Ord a => ListColumn a -> ListColumn a -> Ordering
forall a. Ord a => ListColumn a -> ListColumn a -> ListColumn a
$ccompare :: forall a. Ord a => ListColumn a -> ListColumn a -> Ordering
compare :: ListColumn a -> ListColumn a -> Ordering
$c< :: forall a. Ord a => ListColumn a -> ListColumn a -> Bool
< :: ListColumn a -> ListColumn a -> Bool
$c<= :: forall a. Ord a => ListColumn a -> ListColumn a -> Bool
<= :: ListColumn a -> ListColumn a -> Bool
$c> :: forall a. Ord a => ListColumn a -> ListColumn a -> Bool
> :: ListColumn a -> ListColumn a -> Bool
$c>= :: forall a. Ord a => ListColumn a -> ListColumn a -> Bool
>= :: ListColumn a -> ListColumn a -> Bool
$cmax :: forall a. Ord a => ListColumn a -> ListColumn a -> ListColumn a
max :: ListColumn a -> ListColumn a -> ListColumn a
$cmin :: forall a. Ord a => ListColumn a -> ListColumn a -> ListColumn a
min :: ListColumn a -> ListColumn a -> ListColumn a
Ord, (forall x. ListColumn a -> Rep (ListColumn a) x)
-> (forall x. Rep (ListColumn a) x -> ListColumn a)
-> Generic (ListColumn a)
forall x. Rep (ListColumn a) x -> ListColumn a
forall x. ListColumn a -> Rep (ListColumn a) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (ListColumn a) x -> ListColumn a
forall a x. ListColumn a -> Rep (ListColumn a) x
$cfrom :: forall a x. ListColumn a -> Rep (ListColumn a) x
from :: forall x. ListColumn a -> Rep (ListColumn a) x
$cto :: forall a x. Rep (ListColumn a) x -> ListColumn a
to :: forall x. Rep (ListColumn a) x -> ListColumn a
G.Generic, Int -> ListColumn a -> [Char] -> [Char]
[ListColumn a] -> [Char] -> [Char]
ListColumn a -> [Char]
(Int -> ListColumn a -> [Char] -> [Char])
-> (ListColumn a -> [Char])
-> ([ListColumn a] -> [Char] -> [Char])
-> Show (ListColumn a)
forall a. Show a => Int -> ListColumn a -> [Char] -> [Char]
forall a. Show a => [ListColumn a] -> [Char] -> [Char]
forall a. Show a => ListColumn a -> [Char]
forall a.
(Int -> a -> [Char] -> [Char])
-> (a -> [Char]) -> ([a] -> [Char] -> [Char]) -> Show a
$cshowsPrec :: forall a. Show a => Int -> ListColumn a -> [Char] -> [Char]
showsPrec :: Int -> ListColumn a -> [Char] -> [Char]
$cshow :: forall a. Show a => ListColumn a -> [Char]
show :: ListColumn a -> [Char]
$cshowList :: forall a. Show a => [ListColumn a] -> [Char] -> [Char]
showList :: [ListColumn a] -> [Char] -> [Char]
Show)

getMaybeListColumn :: Maybe (ListColumn a) -> [a]
getMaybeListColumn :: forall a. Maybe (ListColumn a) -> [a]
getMaybeListColumn Maybe (ListColumn a)
Nothing  = []
getMaybeListColumn (Just ListColumn a
x) = ListColumn a -> [a]
forall a. ListColumn a -> [a]
getListColumn ListColumn a
x

instance (Csv.ToField a, Show a) => Csv.ToField (ListColumn a) where
    toField :: ListColumn a -> ByteString
toField ListColumn a
x = ByteString -> [ByteString] -> ByteString
Bchs.intercalate ByteString
";" ([ByteString] -> ByteString) -> [ByteString] -> ByteString
forall a b. (a -> b) -> a -> b
$ (a -> ByteString) -> [a] -> [ByteString]
forall a b. (a -> b) -> [a] -> [b]
L.map a -> ByteString
forall a. ToField a => a -> ByteString
Csv.toField ([a] -> [ByteString]) -> [a] -> [ByteString]
forall a b. (a -> b) -> a -> b
$ ListColumn a -> [a]
forall a. ListColumn a -> [a]
getListColumn ListColumn a
x
instance (Csv.FromField a) => Csv.FromField (ListColumn a) where
    parseField :: ByteString -> Parser (ListColumn a)
parseField ByteString
x = ([a] -> ListColumn a) -> Parser [a] -> Parser (ListColumn a)
forall a b. (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [a] -> ListColumn a
forall a. [a] -> ListColumn a
ListColumn (Parser [a] -> Parser (ListColumn a))
-> ([ByteString] -> Parser [a])
-> [ByteString]
-> Parser (ListColumn a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteString -> Parser a) -> [ByteString] -> Parser [a]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM ByteString -> Parser a
forall a. FromField a => ByteString -> Parser a
Csv.parseField ([ByteString] -> Parser (ListColumn a))
-> [ByteString] -> Parser (ListColumn a)
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> ByteString -> [ByteString]
Bchs.splitWith (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==Char
';') ByteString
x

-- helper functions for reformatting parser errors
removeUselessSuffix :: String -> String
removeUselessSuffix :: [Char] -> [Char]
removeUselessSuffix = Text -> [Char]
T.unpack (Text -> [Char]) -> ([Char] -> Text) -> [Char] -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HasCallStack => Text -> Text -> Text -> Text
Text -> Text -> Text -> Text
T.replace Text
" at \"\"" Text
"" (Text -> Text) -> ([Char] -> Text) -> [Char] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> Text
T.pack

data CsvParseError = CsvParseError {
      CsvParseError -> [Char]
_expected :: String
    , CsvParseError -> [Char]
_actual   :: String
    , CsvParseError -> [Char]
_leftover :: String
} deriving Int -> CsvParseError -> [Char] -> [Char]
[CsvParseError] -> [Char] -> [Char]
CsvParseError -> [Char]
(Int -> CsvParseError -> [Char] -> [Char])
-> (CsvParseError -> [Char])
-> ([CsvParseError] -> [Char] -> [Char])
-> Show CsvParseError
forall a.
(Int -> a -> [Char] -> [Char])
-> (a -> [Char]) -> ([a] -> [Char] -> [Char]) -> Show a
$cshowsPrec :: Int -> CsvParseError -> [Char] -> [Char]
showsPrec :: Int -> CsvParseError -> [Char] -> [Char]
$cshow :: CsvParseError -> [Char]
show :: CsvParseError -> [Char]
$cshowList :: [CsvParseError] -> [Char] -> [Char]
showList :: [CsvParseError] -> [Char] -> [Char]
Show

parseCsvParseError :: P.Parser CsvParseError
parseCsvParseError :: Parser CsvParseError
parseCsvParseError = do
    [Char]
_ <- [Char] -> ParsecT [Char] () Identity [Char]
forall s (m :: * -> *) u.
Stream s m Char =>
[Char] -> ParsecT s u m [Char]
P.string [Char]
"parse error (Failed reading: conversion error: expected "
    [Char]
expected <- ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity [Char]
forall s (m :: * -> *) t u a end.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m [a]
P.manyTill ParsecT [Char] () Identity Char
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Char
P.anyChar (ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity [Char]
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
P.try ([Char] -> ParsecT [Char] () Identity [Char]
forall s (m :: * -> *) u.
Stream s m Char =>
[Char] -> ParsecT s u m [Char]
P.string [Char]
", got "))
    [Char]
actual <- ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity [Char]
forall s (m :: * -> *) t u a end.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m [a]
P.manyTill ParsecT [Char] () Identity Char
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Char
P.anyChar (ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity [Char]
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
P.try ([Char] -> ParsecT [Char] () Identity [Char]
forall s (m :: * -> *) u.
Stream s m Char =>
[Char] -> ParsecT s u m [Char]
P.string [Char]
" (incomplete field parse, leftover: ["))
    [Int]
leftoverList <- ParsecT [Char] () Identity Int
-> ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity [Int]
forall s (m :: * -> *) t u a end.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m [a]
P.sepBy ([Char] -> Int
forall a. Read a => [Char] -> a
read ([Char] -> Int)
-> ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity [Char]
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m [a]
P.many1 ParsecT [Char] () Identity Char
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Char
P.digit) (Char -> ParsecT [Char] () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
P.char Char
',')
    Char
_ <- Char -> ParsecT [Char] () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
P.char Char
']'
    [Char]
_ <- ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity [Char]
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
P.many ParsecT [Char] () Identity Char
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Char
P.anyChar
    CsvParseError -> Parser CsvParseError
forall a. a -> ParsecT [Char] () Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (CsvParseError -> Parser CsvParseError)
-> CsvParseError -> Parser CsvParseError
forall a b. (a -> b) -> a -> b
$ [Char] -> [Char] -> [Char] -> CsvParseError
CsvParseError [Char]
expected [Char]
actual ((Int -> Char) -> [Int] -> [Char]
forall a b. (a -> b) -> [a] -> [b]
L.map Int -> Char
chr [Int]
leftoverList)

renderCsvParseError :: CsvParseError -> String
renderCsvParseError :: CsvParseError -> [Char]
renderCsvParseError (CsvParseError [Char]
expected [Char]
actual [Char]
leftover) =
    [Char]
"parse error in one column (" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++
    [Char]
"expected data type: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
expected [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
", " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++
    [Char]
"broken value: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
actual [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
", " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++
    [Char]
"problematic characters: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char] -> [Char]
forall a. Show a => a -> [Char]
show [Char]
leftover [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
")"