Something went wrong. Try again.
"Leading Comma JSON Formatter"
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116{-# LANGUAGE OverloadedStrings #-}
module LCJF ( prettyLeadingComma,) where
import Data.Aeson (Value (..))import qualified Data.Aeson as Aesonimport qualified Data.Aeson.Key as Keyimport qualified Data.Aeson.KeyMap as KeyMapimport Data.Bifunctor (first)import Data.List (sortOn)import Data.Scientific (Scientific)import qualified Data.Scientific as Scientificimport qualified Data.Text as Timport qualified Data.Vector as V
prettyLeadingComma :: Value -> T.TextprettyLeadingComma = renderValue 0
renderValue :: Int -> Value -> T.TextrenderValue indent value = case value of Null -> "null" Bool True -> "true" Bool False -> "false" Number n -> renderNumber n String s -> renderString s Array arr -> renderArray indent arr Object obj -> renderObject indent obj
renderNumber :: Scientific -> T.TextrenderNumber n | Scientific.isInteger n = T.pack (show (floor n :: Integer)) | otherwise = T.pack (show n)
renderString :: T.Text -> T.TextrenderString s = "\"" <> escapeString s <> "\""
escapeString :: T.Text -> T.TextescapeString = T.concatMap escapeChar where escapeChar :: Char -> T.Text escapeChar c = case c of '"' -> "\\\"" '\\' -> "\\\\" '\n' -> "\\n" '\r' -> "\\r" '\t' -> "\\t" _ | c < ' ' -> T.pack ("\\u" <> pad4 (showHex' (fromEnum c))) | otherwise -> T.singleton c
showHex' :: Int -> String showHex' n = showHexDigits n ""
showHexDigits :: Int -> String -> String showHexDigits 0 acc = if null acc then "0" else acc showHexDigits m acc = showHexDigits (m `div` 16) (hexDigit (m `mod` 16) : acc)
hexDigit :: Int -> Char hexDigit d | d < 10 = toEnum (fromEnum '0' + d) | otherwise = toEnum (fromEnum 'a' + d - 10)
pad4 :: String -> String pad4 s = replicate (4 - length s) '0' <> s
indentStr :: Int -> T.TextindentStr n = T.replicate n (T.singleton ' ')
renderArray :: Int -> V.Vector Value -> T.TextrenderArray indent arr | V.null arr = "[ ]" | otherwise = let items = V.toList arr renderedItems = map (renderValue (indent + 2)) items firstLine = "[ " <> head renderedItems restLines = map (", " <>) (tail renderedItems) allLines = firstLine : restLines ind = indentStr indent in T.intercalate ("\n" <> ind) allLines <> "\n" <> ind <> "]"
renderObject :: Int -> Aeson.Object -> T.TextrenderObject indent obj | KeyMap.null obj = "{ }" | otherwise = let pairs = sortOn fst (map (first Key.toText) (KeyMap.toList obj)) renderedPairs = map (renderPair indent) pairs firstLine = "{ " <> head renderedPairs restLines = map (", " <>) (tail renderedPairs) allLines = firstLine : restLines ind = indentStr indent in T.intercalate ("\n" <> ind) allLines <> "\n" <> ind <> "}"
renderPair :: Int -> (T.Text, Value) -> T.TextrenderPair indent (key, value) = let renderedKey = renderString key renderedValue = renderValue (indent + 2) value in case value of Array _ | not (isEmptyArray value) -> renderedKey <> ":\n" <> indentStr (indent + 2) <> renderedValue Object _ | not (isEmptyObject value) -> renderedKey <> ":\n" <> indentStr (indent + 2) <> renderedValue _ -> renderedKey <> ": " <> renderedValue
isEmptyArray :: Value -> BoolisEmptyArray (Array arr) = V.null arrisEmptyArray _ = False
isEmptyObject :: Value -> BoolisEmptyObject (Object obj) = KeyMap.null objisEmptyObject _ = False