nestedtext-0.1.3: src/Data/NestedText/Serialize.hs
{-# LANGUAGE DeriveGeneric #-}
module Data.NestedText.Serialize
( serialize
, serialize'
) where
import Data.NestedText.Type
import Data.NestedText.Util
import qualified Data.Char as C
import Data.Functor.Identity (Identity(..))
import Data.List (intersperse)
import qualified Data.Map.Strict as M
import Data.Maybe (isJust)
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TLB
import qualified Data.Text.Short as ST
import qualified Data.Vector as V
import Generic.Data
import Lens.Micro.Platform
import qualified Pipes.Text as PT
data Denote
= Denote'Null
| Denote'SingleLine T.Text
| Denote'MultipleLine Item
deriving (Generic)
serialize :: Int -> Item -> TL.Text
serialize indentWidth = TLB.toLazyText . write
where
indent i = TLB.fromString $ replicate i ' '
nl = TLB.fromText osNewline
isMultilineKey ts =
isJust (ST.findIndex (== '\n') ts)
|| ST.null ts
|| (case ST.uncons ts of
Nothing -> False
Just (c, _) -> C.isSpace c || S.member c specialCharsDict || c == '#')
|| (case ST.unsnoc ts of
Nothing -> False
Just (_, c) -> C.isSpace c || c == ':')
|| T.isInfixOf (T.pack ": ") (ST.toText ts)
denoteItem item@(Item'String ts) = case T.findIndex (== '\n') ts of
Just _ -> Denote'MultipleLine item
Nothing -> if T.null ts then Denote'Null else Denote'SingleLine ts
denoteItem item = Denote'MultipleLine item
write i = writeItem 0 i <> nl
writeItem i (Item'String ts) = writeString i ts
writeItem i (Item'List vs) = if V.null vs
then indent i <> TLB.fromString "[]"
else mconcat $ intersperse nl
$ flip map (V.toList vs) $ \vi -> case denoteItem vi of
Denote'Null -> indent i <> TLB.fromString "-"
Denote'SingleLine ts -> indent i <> TLB.fromString "- " <> TLB.fromText ts
Denote'MultipleLine _ -> indent i <> TLB.fromString "-"
<> nl <> writeItem (i + indentWidth) vi
writeItem i (Item'Dictionary dic) = if M.null dic
then indent i <> TLB.fromString "{}"
else mconcat $ intersperse nl
$ flip map (M.toAscList dic) $ \(k, vi) -> if isMultilineKey k
then writeKey i k <> nl <> writeItem (i + indentWidth) vi
else case denoteItem vi of
Denote'Null ->
indent i <> TLB.fromText (ST.toText k) <> TLB.fromString ":"
Denote'SingleLine ts ->
indent i <> TLB.fromText (ST.toText k) <> TLB.fromString ": "
<> TLB.fromText ts
Denote'MultipleLine _ ->
indent i <> TLB.fromText (ST.toText k) <> TLB.fromString ":"
<> nl <> writeItem (i + indentWidth) vi
writeString i ts =
freeTToLines (splitLines $ PT.fromLazy $ TL.fromStrict ts)
& runIdentity
& map (\l -> if T.null l
then indent i <> TLB.fromString ">"
else indent i <> TLB.fromString "> " <> TLB.fromText l)
& intersperse nl
& mconcat
writeKey i ts =
freeTToLines (splitLines $ PT.fromLazy $ TL.fromStrict $ ST.toText ts)
& runIdentity
& map (\l -> if T.null l
then indent i <> TLB.fromString ":"
else indent i <> TLB.fromString ": " <> TLB.fromText l)
& intersperse nl
& mconcat
serialize' :: Int -> Item -> T.Text
serialize' i = TL.toStrict . serialize i