aeson-yaml 1.0.5.0 → 1.0.6.0
raw patch · 4 files changed
+81/−28 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +8/−0
- aeson-yaml.cabal +1/−1
- src/Data/Aeson/Yaml.hs +21/−13
- test/Test/Data/Aeson/Yaml.hs +51/−14
CHANGELOG.md view
@@ -1,5 +1,13 @@ # Changelog +## 1.0.6.0 - 2020-02-27++### Changed++ - Don't quote simple strings containing spaces, e.g: hello world++ - Single-quote dates (like '2020-02-27') and bools ('true' / 'false')+ ## 1.0.5.0 - 2019-11-30 ### Fixed
aeson-yaml.cabal view
@@ -1,7 +1,7 @@ cabal-version: 1.12 name: aeson-yaml-version: 1.0.5.0+version: 1.0.6.0 homepage: https://github.com/clovyr/aeson-yaml bug-reports: https://github.com/clovyr/aeson-yaml/issues author: Patrick Nielsen
src/Data/Aeson/Yaml.hs view
@@ -24,7 +24,7 @@ import qualified Data.ByteString.Builder as ByteString.Builder import qualified Data.ByteString.Lazy as ByteString.Lazy import qualified Data.ByteString.Short as ByteString.Short-import Data.Char (isDigit)+import Data.Char (isAlpha, isDigit) import qualified Data.HashMap.Strict as HashMap import Data.List (intersperse, sortOn) import Data.Monoid ((<>), mconcat, mempty)@@ -120,18 +120,28 @@ encodeText :: Bool -> Bool -> Int -> Text -> Builder encodeText canMultiline alwaysQuote level s+ -- s is a value, not a map key, and contains newlines; can be inserted+ -- literally with `|` syntax | canMultiline && "\n" `Text.isSuffixOf` s = encodeLines level (Text.lines s)- | alwaysQuote && unquotable =- bs "'" <> b (Text.Encoding.encodeUtf8 s) <> bs "'"+ -- s is a number, date, or boolString; single-quote+ | Text.all isNumberOrDateRelated s || isBoolString = singleQuote+ -- s should be quoted, AND s is not unsafe; single-quote+ | alwaysQuote && unquotable = singleQuote+ -- s should be quoted, OR s might be unsafe; double-quote | alwaysQuote || not unquotable = bl $ Data.Aeson.encode s- | otherwise = b (Text.Encoding.encodeUtf8 s)+ -- otherwise; no quotes+ | otherwise = noQuote where- unquotable =- s /= "" &&- not isSpecial &&- isSafeAscii (Text.head s) &&- not (Text.all isNumberOrDateRelated s) && Text.all isAllowed s- isSpecial+ noQuote = b (Text.Encoding.encodeUtf8 s)+ singleQuote = bs "'" <> noQuote <> bs "'"+ headS = Text.head s+ unquotable -- s is unquotable if all are True+ =+ s /= "" && -- s is not empty+ Text.all isAllowed s && -- s consists of acceptable chars+ (Data.Char.isAlpha headS || -- head of s is a char in A-Z or a-z or indicates a filepath+ headS == '/')+ isBoolString | Text.length s > 5 = False | otherwise = case Text.toLower s of@@ -143,9 +153,7 @@ (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '/' || c == '_' || c == '.' || c == '=' isNumberOrDateRelated c = isDigit c || c == '.' || c == 'e' || c == '-'- isAllowed c- -- We don't include ' ' here to avoid sequences like " -" and ": "- = isSafeAscii c || c == '-' || c == ':'+ isAllowed c = isSafeAscii c || c == '-' || c == ':' || c == ' ' encodeLines :: Int -> [Text] -> Builder encodeLines level ls =
test/Test/Data/Aeson/Yaml.hs view
@@ -20,19 +20,19 @@ import Data.Aeson.Yaml -data TestCase =- TestCase- { tcName :: String- , tcInput :: Data.ByteString.Lazy.ByteString- , tcOutput :: Data.ByteString.Lazy.ByteString- , tcAlwaysQuote :: Bool- }+data TestCase = TestCase+ { tcName :: String+ , tcInput :: Data.ByteString.Lazy.ByteString+ , tcOutput :: Data.ByteString.Lazy.ByteString+ , tcAlwaysQuote :: Bool+ } testCases :: [TestCase]-testCases = [tcDataTypes, tcNestedListsAndObjects, tcQuoted, tcEmptyList]+testCases =+ [tcDataTypes, tcNestedListsAndObjects, tcQuoted, tcEmptyList, tcHelloWorld] tcEmptyList :: TestCase-tcEmptyList = +tcEmptyList = TestCase { tcName = "Empty root list" , tcInput = "[]"@@ -57,6 +57,8 @@ "dateString": "2038-01-19", "piString": "3.14", "expString": "1e3",+ "leadingSpace" : " leading space",+ "leadingSymbol" : "!leading symbol", "asteriskString": "*", "multiLine": "The first line is followed by the\nsecond line\n", "multiLineWithSpaces": " This has extra\n spaces at the beginning\n",@@ -67,12 +69,14 @@ |] , tcOutput = [s|asteriskString: "*"-boolString: "true"-dateString: "2038-01-19"+boolString: 'true'+dateString: '2038-01-19' emptyObject: {}-expString: "1e3"+expString: '1e3' isFalse: false isTrue: true+leadingSpace: " leading space"+leadingSymbol: "!leading symbol" list: - foo - bar@@ -86,8 +90,8 @@ spaces at the beginning notMultiline: "This won't be\nmulti-lined" nullValue: null-numberString: "12345"-piString: "3.14"+numberString: '12345'+piString: '3.14' "quoted ! key": true |] , tcAlwaysQuote = False@@ -202,6 +206,39 @@ 'foo': 'bar' |] , tcAlwaysQuote = True+ }++tcHelloWorld :: TestCase+tcHelloWorld =+ TestCase+ { tcName = "Hello World"+ , tcInput =+ [s|+{ "image": "node 10.15.3"+, "pipelines":+ { "default":+ [ { "step":+ { "caches": []+ , "name": "hello world"+ , "script": [ "echo hello" ]+ }+ }+ ]+ + }+}+|]+ , tcOutput =+ [s|image: node 10.15.3+pipelines:+ default:+ - step:+ caches: []+ name: hello world+ script:+ - echo hello+|]+ , tcAlwaysQuote = False } foo :: Data.Aeson.Value