module Beautifier
( beautify
, indent
, firstString
, splitAtHead
) where
import Data.Int (Int64)
import Data.Monoid ((<>))
import qualified Data.Text.Lazy as T
type IndentationLevel = Int64
indentation :: T.Text
indentation = " "
newline :: T.Text
newline = "\n"
indent :: IndentationLevel -> T.Text -> T.Text
indent level str = T.replicate level indentation <> str
trimmedTail :: T.Text -> T.Text
trimmedTail = T.stripStart . T.tail
splitAtHead :: T.Text -> (T.Text, T.Text)
splitAtHead "" = ("", "")
splitAtHead str@(T.head -> '\\') = T.splitAt 2 str
splitAtHead str = T.splitAt 1 str
string :: Bool -> T.Text -> T.Text
string False str@(T.head -> '"') = "\"" <> string True (T.tail str)
string True str@(T.head -> '"') = "\""
string True str = let (head, tail) = splitAtHead str in head <> string True tail
firstString :: T.Text -> T.Text
firstString str = string False str
isWhitespace x
| x == ' ' = True
| x == '\r' = True
| x == '\n' = True
| x == '\t' = True
| otherwise = False
isEndOfValue x
| isWhitespace x = True
| x == ',' = True
| x == '}' = True
| x == ']' = True
| otherwise = False
extractJsonValue :: T.Text -> T.Text
extractJsonValue = T.takeWhile (not . isEndOfValue)
beautifyText :: IndentationLevel -> T.Text -> T.Text
beautifyText i str
| str == "" = ""
| head == ' ' = stripStartThenParse str
| head == '\n' = stripStartThenParse str
| head == '\t' = stripStartThenParse str
| head == '{' = nextLineAfterOpening "{"
| head == '[' = nextLineAfterOpening "["
| head == '}' = nextLineAfterClosing "}"
| head == ']' = nextLineAfterClosing "]"
| head == ',' = "," <> newline <> indent i (beautifyText i (trimmedTail str))
| head == '"' = let groupedString = firstString str
restOfTheString = T.drop (T.length groupedString) str
in groupedString <> beautifyText i restOfTheString
| head == ':' = ": " <> beautifyText i (trimmedTail str)
| otherwise = let value = extractJsonValue str
in value <> beautifyText i (T.drop (T.length value) str)
where
head = T.head str
stripStartThenParse = beautifyText i . T.stripStart
nextLineAfterOpening token = token
<> newline
<> indent (i + 1) (beautifyText (i + 1) (trimmedTail str))
nextLineAfterClosing token = newline
<> indent (i 1) token
<> beautifyText (i 1) (trimmedTail str)
beautify :: T.Text -> T.Text
beautify = beautifyText 0