bunz 0.0.7 → 0.0.9
raw patch · 5 files changed
+71/−99 lines, 5 files
Files
- README.md +4/−2
- app/Main.hs +4/−3
- bunz.cabal +2/−2
- src/Beautifier.hs +49/−81
- test/BeautifierSpec.hs +12/−11
README.md view
@@ -3,12 +3,14 @@ ## Usage ```-λ bunz [master] + bunz "{\"foo\":\"bar\"}"+// Beautify plain json string+bunz "{\"foo\":\"bar\"}" { "foo": "bar" } -λ bunz [master] + cat test.json | bunz+// Beautify the input+cat test.json | bunz { "name": "sendy", "popular": false,
app/Main.hs view
@@ -3,8 +3,8 @@ module Main where import qualified Beautifier as B-import qualified Data.Text.Lazy as T-import qualified Data.Text.Lazy.IO as TextIO+import qualified Data.Text as T+import qualified Data.Text.IO as TextIO import Data.Version (showVersion) import Paths_bunz (version) import System.Console.CmdArgs ((&=))@@ -28,9 +28,10 @@ runFromStdInput :: IO () runFromStdInput = TextIO.getContents >>= printBeautified + runFromArgs :: Args -> IO () runFromArgs (Args { jsonString = str }) = printBeautified $ T.pack str run :: Bool -> IO () run False = runFromStdInput-run True = CA.cmdArgs args >>= runFromArgs+run True = CA.cmdArgs args >>= runFromArgs
bunz.cabal view
@@ -1,5 +1,5 @@ name: bunz-version: 0.0.7+version: 0.0.9 synopsis: CLI tool to beautify JSON string. description: CLI tool to beautify JSON string. homepage: https://github.com/sendyhalim/bunz@@ -7,7 +7,7 @@ license-file: LICENSE author: Sendy Halim <sendyhalim93@gmail.com> maintainer: Sendy Halim <sendyhalim93@gmail.com>-copyright: (c) 2017 Sendy Halim+copyright: (c) 2019 Sendy Halim category: CLI, JSON build-type: Simple extra-source-files: README.md
src/Beautifier.hs view
@@ -3,64 +3,32 @@ module Beautifier ( beautify- , indent+ , indentation , firstString , splitAtHead ) where -import Data.Int (Int64)-import Data.Monoid ((<>))-import qualified Data.Text.Lazy as T-import qualified Data.Text.Lazy.Builder as B+import Data.Int (Int64)+import Data.Monoid ((<>))+import qualified Data.Text as T -- Doctest setup -- $setup -- >>> :set -XOverloadedStrings -type IndentationLevel = Int64----- | Lift function that works with Data.Text.Lazy--- to be able to work with Data.Text.Lazy.Builder.--- There should be a better and faster way to manipulate builder. We're using--- this for now because it's a lot faster than just using plain Data.Text.Lazy when--- concatenating text.-liftToBuilder :: (T.Text -> T.Text) -> (B.Builder -> B.Builder)-liftToBuilder f = B.fromLazyText . f . B.toLazyText--splitAt' :: Int64 -> B.Builder -> (B.Builder, B.Builder)-splitAt' n str = (B.fromLazyText head, B.fromLazyText tail)- where (head, tail) = T.splitAt n (B.toLazyText str)--head' :: B.Builder -> B.Builder-head' = B.singleton . T.head . B.toLazyText--tail' :: B.Builder -> B.Builder-tail' = liftToBuilder T.tail--drop' :: Int64 -> B.Builder -> B.Builder-drop' length = liftToBuilder (T.drop length)--length' :: B.Builder -> Int64-length' = T.length . B.toLazyText--stripStart' :: B.Builder -> B.Builder-stripStart' = liftToBuilder T.stripStart+type IndentationLevel = Int spaceIndentation :: T.Text spaceIndentation = " " -indentation :: IndentationLevel -> B.Builder-indentation level = B.fromLazyText (T.replicate level spaceIndentation)--newline :: B.Builder-newline = B.fromLazyText "\n"+indentation :: IndentationLevel -> T.Text+indentation level = (T.replicate level spaceIndentation) -indent :: IndentationLevel -> B.Builder -> B.Builder-indent level str = indentation level <> str+newline :: T.Text+newline = "\n" -trimmedTail :: B.Builder -> B.Builder-trimmedTail = B.fromLazyText . T.stripStart . T.tail . B.toLazyText+trimmedTail :: T.Text -> T.Text+trimmedTail = T.stripStart . T.tail -- | Split the given string at its head while honouring escaped string. -- Examples:@@ -70,25 +38,29 @@ -- -- >>> splitAtHead "\nyeay" -- ("\n","yeay")-splitAtHead :: B.Builder -> (B.Builder, B.Builder)-splitAtHead "" = ("", "")-splitAtHead str@(head' -> "\\") = splitAt' 2 str-splitAtHead str = splitAt' 1 str+splitAtHead :: T.Text -> (T.Text, T.Text)+splitAtHead "" = ("", "")+splitAtHead str@(T.head -> '\\') = T.splitAt 2 str+splitAtHead str = T.splitAt 1 str -- | Extract string within double quotes `"` including the double quotes ignoring--- the suffix after closing quote `"`+-- the suffix after closing quote `"`. It will ignore unescaped newline within the string. -- -- Examples: -- -- >>> string False "\"test\"whatuppp" -- "\"test\""-string :: Bool -> B.Builder -> B.Builder-string False str@(head' -> "\"") = "\"" <> string True (tail' str)-string True str@(head' -> "\"") = "\""-string True str = let (head, tail) = splitAtHead str in head <> string True tail+--+-- >>> string False "\"\ntest\"whatuppp"+-- "\"test\""+string :: Bool -> T.Text -> [T.Text]+string flag str@(T.head -> '\n') = string flag (T.tail str)+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 -- | Extract the first string value starting from the front.-firstString :: B.Builder -> B.Builder+firstString :: T.Text -> [T.Text] firstString str = string False str @@ -117,40 +89,36 @@ -- -- >>> extractJsonValue "false,true,false]" -- "false"-extractJsonValue :: B.Builder -> B.Builder-extractJsonValue = liftToBuilder $ T.takeWhile (not . isEndOfValue)+extractJsonValue :: T.Text -> T.Text+extractJsonValue = T.takeWhile (not . isEndOfValue) -- |Beautify the given JSON text based on the current indentation level.-beautifyText :: IndentationLevel -> B.Builder -> B.Builder+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 = drop' (length' groupedString) str- in groupedString <> beautifyText i restOfTheString- | head == ":" = ": " <> beautifyText i (trimmedTail str)+ | str == "" = [""]+ | head == ' ' = stripStartThenParse str+ | head == '\n' = stripStartThenParse str+ | head == '\t' = stripStartThenParse str+ | head == '{' = nextLineAfterOpening "{"+ | head == '[' = nextLineAfterOpening "["+ | head == '}' = nextLineAfterClosing "}"+ | head == ']' = nextLineAfterClosing "]"+ | head == ',' = [",", newline, indentation i] ++ (beautifyText i (trimmedTail str))+ | head == '"' =+ let groupedString = mconcat $ firstString str+ restOfTheString = T.drop (T.length groupedString) str+ in [groupedString] ++ beautifyText i restOfTheString+ | head == ':' = [": "] ++ beautifyText i (trimmedTail str) | otherwise = let value = extractJsonValue str- valueLength = length' value- in value <> beautifyText i (drop' valueLength str)+ valueLength = T.length value+ in [value] ++ beautifyText i (T.drop valueLength str) where- head = head' str- stripStartThenParse = beautifyText i . 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)+ head = T.head str+ stripStartThenParse = beautifyText i . T.stripStart+ nextLineAfterOpening token = [token , newline , indentation (i + 1)] ++ (beautifyText (i + 1) (trimmedTail str))+ nextLineAfterClosing token = [newline, indentation (i - 1), token] ++ beautifyText (i - 1) (trimmedTail str) beautify :: T.Text -> T.Text-beautify = B.toLazyText . beautifyText 0 . B.fromLazyText+beautify = mconcat . beautifyText 0
test/BeautifierSpec.hs view
@@ -2,7 +2,8 @@ module BeautifierSpec (main, spec) where -import qualified Beautifier as B+import qualified Beautifier as B+import Data.Monoid ((<>)) import Test.Hspec main :: IO()@@ -10,18 +11,18 @@ spec :: Spec spec = do- describe "Beautifier.indent" $ do- context "When given negative indent level" $ do- it "should not indent" $ do- B.indent (-1) "{" `shouldBe` "{"+ describe "Beautifier.indentation" $ do+ context "When given negative indentation level" $ do+ it "should not error" $ do+ B.indentation (-1) <> "{" `shouldBe` "{" - context "When given 0 indent level" $ do- it "should not indent" $ do- B.indent 0 "{" `shouldBe` "{"+ context "When given 0 indentation level" $ do+ it "should not indentation" $ do+ B.indentation 0 <> "{" `shouldBe` "{" - context "When given 2 indent level" $ do- it "should indent by 2 levels" $ do- B.indent 2 "{" `shouldBe` " {"+ context "When given 2 indentation level" $ do+ it "should indentation by 2 levels" $ do+ B.indentation 2 <> "{" `shouldBe` " {" describe "Beautifier.splitAtHead" $ do context "when text first character is escaped" $ do