module Circuit.Parser.Csv
(
Csv (..),
csv,
record,
field,
decodeCsv,
)
where
import Circuit.Parser
import Control.Monad (void)
import Data.ByteString (ByteString)
import Data.Functor (($>))
import Data.Functor.Identity (Identity)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Text.Encoding (decodeUtf8')
import Data.Vector (Vector)
import Data.Vector qualified as V
newtype Csv = Csv (Vector (Vector Text))
deriving (Csv -> Csv -> Bool
(Csv -> Csv -> Bool) -> (Csv -> Csv -> Bool) -> Eq Csv
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Csv -> Csv -> Bool
== :: Csv -> Csv -> Bool
$c/= :: Csv -> Csv -> Bool
/= :: Csv -> Csv -> Bool
Eq, Int -> Csv -> ShowS
[Csv] -> ShowS
Csv -> String
(Int -> Csv -> ShowS)
-> (Csv -> String) -> ([Csv] -> ShowS) -> Show Csv
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Csv -> ShowS
showsPrec :: Int -> Csv -> ShowS
$cshow :: Csv -> String
show :: Csv -> String
$cshowList :: [Csv] -> ShowS
showList :: [Csv] -> ShowS
Show)
csv :: Parser Identity ByteString Char Csv
csv :: Parser Identity ByteString Char Csv
csv = (Parser Identity ByteString Char ()
forall (m :: * -> *) f s. (Monad m, Uncons f s) => Parser m f s ()
endOfInput Parser Identity ByteString Char ()
-> Csv -> Parser Identity ByteString Char Csv
forall (f :: * -> *) a b. Functor f => f a -> b -> f b
$> Vector (Vector Text) -> Csv
Csv Vector (Vector Text)
forall a. Vector a
V.empty) Parser Identity ByteString Char Csv
-> Parser Identity ByteString Char Csv
-> Parser Identity ByteString Char Csv
forall a.
Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Identity ByteString Char Csv
rows
where
rows :: Parser Identity ByteString Char Csv
rows = do
r <- Parser Identity ByteString Char [Text]
record
Csv . V.fromList . map V.fromList <$> loop [r]
loop :: [[Text]] -> Parser Identity ByteString Char [[Text]]
loop [[Text]]
acc =
( do
_ <- Parser Identity ByteString Char ()
eol
atEnd <- (endOfInput $> True) <|> pure False
if atEnd
then pure (reverse acc)
else do
r <- record
loop (r : acc)
)
Parser Identity ByteString Char [[Text]]
-> Parser Identity ByteString Char [[Text]]
-> Parser Identity ByteString Char [[Text]]
forall a.
Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> [[Text]] -> Parser Identity ByteString Char [[Text]]
forall a. a -> Parser Identity ByteString Char a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([[Text]] -> [[Text]]
forall a. [a] -> [a]
reverse [[Text]]
acc)
record :: Parser Identity ByteString Char [Text]
record :: Parser Identity ByteString Char [Text]
record = do
f <- Parser Identity ByteString Char Text
field
loop [f]
where
loop :: [Text] -> Parser Identity ByteString Char [Text]
loop [Text]
acc =
( do
_ <- Char -> Parser Identity ByteString Char Char
forall (m :: * -> *) f s.
(Monad m, Uncons f s, Eq s) =>
s -> Parser m f s s
char Char
','
f <- field
loop (f : acc)
)
Parser Identity ByteString Char [Text]
-> Parser Identity ByteString Char [Text]
-> Parser Identity ByteString Char [Text]
forall a.
Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> [Text] -> Parser Identity ByteString Char [Text]
forall a. a -> Parser Identity ByteString Char a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Text] -> [Text]
forall a. [a] -> [a]
reverse [Text]
acc)
field :: Parser Identity ByteString Char Text
field :: Parser Identity ByteString Char Text
field = Parser Identity ByteString Char Text
-> Parser Identity ByteString Char Text
forall (m :: * -> *) f s a.
(Monad m, Uncons f s) =>
Parser m f s a -> Parser m f s a
try Parser Identity ByteString Char Text
quoted Parser Identity ByteString Char Text
-> Parser Identity ByteString Char Text
-> Parser Identity ByteString Char Text
forall a.
Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Identity ByteString Char Text
plain
where
plain :: Parser Identity ByteString Char Text
plain = do
raw <- Parser Identity ByteString Char ()
-> Parser Identity ByteString Char ByteString
forall (m :: * -> *) a.
Monad m =>
Parser m ByteString Char a -> Parser m ByteString Char ByteString
bs ((Char -> Bool) -> Parser Identity ByteString Char ()
forall (m :: * -> *) f s.
(Monad m, Uncons f s) =>
(s -> Bool) -> Parser m f s ()
skipWhile (\Char
c -> Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
',' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\r' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\n' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'"'))
either (const empty) pure (decodeUtf8' raw)
quoted :: Parser Identity ByteString Char Text
quoted = do
_ <- Char -> Parser Identity ByteString Char Char
forall (m :: * -> *) f s.
(Monad m, Uncons f s, Eq s) =>
s -> Parser m f s s
char Char
'"'
raw <- bs (skipMany (void (string "\"\"") <|> void (satisfy (/= '"'))))
_ <- char '"'
either (const empty) pure (unquote raw)
unquote :: ByteString -> Either String Text
unquote :: ByteString -> Either String Text
unquote ByteString
raw = case ByteString -> Either UnicodeException Text
decodeUtf8' ByteString
raw of
Left UnicodeException
e -> String -> Either String Text
forall a b. a -> Either a b
Left (UnicodeException -> String
forall a. Show a => a -> String
show UnicodeException
e)
Right Text
t -> Text -> Either String Text
forall a b. b -> Either a b
Right (Text -> [Text] -> Text
T.intercalate (String -> Text
T.pack String
"\"") (HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
T.splitOn (String -> Text
T.pack String
"\"\"") Text
t))
eol :: Parser Identity ByteString Char ()
eol :: Parser Identity ByteString Char ()
eol = Parser Identity ByteString Char String
-> Parser Identity ByteString Char ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (String -> Parser Identity ByteString Char String
forall (m :: * -> *) f s.
(Monad m, Uncons f s, Eq s) =>
[s] -> Parser m f s [s]
string String
"\r\n") Parser Identity ByteString Char ()
-> Parser Identity ByteString Char ()
-> Parser Identity ByteString Char ()
forall a.
Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Identity ByteString Char Char
-> Parser Identity ByteString Char ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Char -> Parser Identity ByteString Char Char
forall (m :: * -> *) f s.
(Monad m, Uncons f s, Eq s) =>
s -> Parser m f s s
char Char
'\n') Parser Identity ByteString Char ()
-> Parser Identity ByteString Char ()
-> Parser Identity ByteString Char ()
forall a.
Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Identity ByteString Char Char
-> Parser Identity ByteString Char ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Char -> Parser Identity ByteString Char Char
forall (m :: * -> *) f s.
(Monad m, Uncons f s, Eq s) =>
s -> Parser m f s s
char Char
'\r')
decodeCsv :: ByteString -> Either String Csv
decodeCsv :: ByteString -> Either String Csv
decodeCsv ByteString
s = case Parser Identity ByteString Char Csv
-> ByteString -> These Csv ByteString
forall {k} f (s :: k) a. Parser Identity f s a -> f -> These a f
runParserIdentity (Parser Identity ByteString Char Csv
csv Parser Identity ByteString Char Csv
-> Parser Identity ByteString Char ()
-> Parser Identity ByteString Char Csv
forall a b.
Parser Identity ByteString Char a
-> Parser Identity ByteString Char b
-> Parser Identity ByteString Char a
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser Identity ByteString Char ()
forall (m :: * -> *) f s. (Monad m, Uncons f s) => Parser m f s ()
endOfInput) ByteString
s of
This Csv
c -> Csv -> Either String Csv
forall a b. b -> Either a b
Right Csv
c
These Csv
c ByteString
_ -> Csv -> Either String Csv
forall a b. b -> Either a b
Right Csv
c
That ByteString
_ -> String -> Either String Csv
forall a b. a -> Either a b
Left String
"invalid CSV"