Skip to content

Commit 330744c

Browse files
committed
Merge branch 'fix-json-escaping-2'
2 parents 344ee9e + 1dceb01 commit 330744c

File tree

9 files changed

+86
-35
lines changed

9 files changed

+86
-35
lines changed

core-data/core-data.cabal

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cabal-version: 1.12
55
-- see: https://github.com/sol/hpack
66

77
name: core-data
8-
version: 0.3.2.1
8+
version: 0.3.2.2
99
synopsis: Convenience wrappers around common data structures and encodings
1010
description: Wrappers around common data structures and encodings.
1111
.
@@ -46,7 +46,7 @@ library
4646
, base >=4.11 && <5
4747
, bytestring
4848
, containers
49-
, core-text >=0.3.4
49+
, core-text >=0.3.7
5050
, hashable >=1.2
5151
, prettyprinter >=1.6.2
5252
, scientific

core-data/lib/Core/Encoding/Json.hs

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ module Core.Encoding.Json (
6767
prettyValue,
6868
) where
6969

70+
#if MIN_VERSION_aeson(2,0,1)
71+
import qualified Data.Aeson.Key as Aeson
72+
import qualified Data.Aeson.KeyMap as Aeson
73+
#else
74+
import qualified Data.HashMap.Strict as HashMap
75+
#endif
76+
7077
import Core.Data.Structures (Key, Map, fromMap, intoMap)
7178
import Core.Text.Bytes (Bytes, fromBytes, intoBytes)
7279
import Core.Text.Colour (
@@ -86,21 +93,15 @@ import Core.Text.Rope (
8693
fromRope,
8794
intoRope,
8895
singletonRope,
96+
unconsRope,
8997
)
9098
import Core.Text.Utilities (
9199
Render (Token, colourize, highlight),
92-
breakPieces,
100+
breakRope,
93101
)
94-
import qualified Data.Aeson as Aeson
95-
96-
#if MIN_VERSION_aeson(2,0,1)
97-
import qualified Data.Aeson.Key as Aeson
98-
import qualified Data.Aeson.KeyMap as Aeson
99-
#else
100-
import qualified Data.HashMap.Strict as HashMap
101-
#endif
102-
103102
import Data.Aeson (FromJSON, Value (String))
103+
import qualified Data.Aeson as Aeson
104+
import Data.Char (intToDigit)
104105
import Data.Coerce
105106
import Data.Hashable (Hashable)
106107
import qualified Data.List as List
@@ -178,26 +179,34 @@ encodeToRope value = case value of
178179
closebracket = singletonRope ']'
179180

180181
{- |
181-
Escape any quotes or backslashes in a JsonString.
182+
Escape any quotes, backslashes, or other possible rubbish in a 'JsonString'.
182183
-}
183184
escapeString :: Rope -> Rope
184185
escapeString text =
185-
let text1 = escapeBackslashes text
186-
text2 = escapeQuotes text1
187-
in text2
186+
let (before, after) = breakRope needsEscaping text
187+
in case unconsRope after of
188+
Nothing ->
189+
text
190+
Just (c, after') ->
191+
before <> escapeCharacter c <> escapeString after'
192+
where
193+
needsEscaping c =
194+
c == '\"' || c == '\\' || c < '\x20'
188195
{-# INLINEABLE escapeString #-}
189196

190-
escapeBackslashes :: Rope -> Rope
191-
escapeBackslashes text =
192-
let pieces = breakPieces (== '\\') text
193-
in mconcat (List.intersperse "\\\\" pieces)
194-
{-# INLINEABLE escapeBackslashes #-}
195-
196-
escapeQuotes :: Rope -> Rope
197-
escapeQuotes text =
198-
let pieces = breakPieces (== '"') text
199-
in mconcat (List.intersperse "\\\"" pieces)
200-
{-# INLINEABLE escapeQuotes #-}
197+
escapeCharacter :: Char -> Rope
198+
escapeCharacter c =
199+
case c of
200+
'\"' -> "\\\""
201+
'\\' -> "\\\\"
202+
'\n' -> "\\n"
203+
'\r' -> "\\r"
204+
'\t' -> "\\t"
205+
_ ->
206+
if c < '\x10'
207+
then "\\u000" <> singletonRope (intToDigit (fromEnum c))
208+
else "\\u001" <> singletonRope (intToDigit ((fromEnum c) - 16))
209+
{-# INLINEABLE escapeCharacter #-}
201210

202211
{- |
203212
Given an array of bytes, attempt to decode it as a JSON value.

core-data/package.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: core-data
2-
version: 0.3.2.1
2+
version: 0.3.2.2
33
synopsis: Convenience wrappers around common data structures and encodings
44
description: |
55
Wrappers around common data structures and encodings.
@@ -33,7 +33,7 @@ dependencies:
3333
- text
3434
- unordered-containers
3535
- vector
36-
- core-text >= 0.3.4
36+
- core-text >= 0.3.7
3737

3838
library:
3939
source-dirs: lib

core-program/lib/Core/Program/Metadata.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,12 @@ readCabalFile = runIO $ do
150150

151151
parseCabalFile :: Bytes -> Map Rope Rope
152152
parseCabalFile contents =
153-
let breakup = intoMap . fmap (breakRope (== ':')) . breakLines . fromBytes
153+
let breakup = intoMap . fmap (breakRope' (== ':')) . breakLines . fromBytes
154154
in breakup contents
155155

156156
-- this should probably be a function in Core.Text.Rope
157-
breakRope :: (Char -> Bool) -> Rope -> (Rope, Rope)
158-
breakRope predicate text =
157+
breakRope' :: (Char -> Bool) -> Rope -> (Rope, Rope)
158+
breakRope' predicate text =
159159
let pieces = take 2 (breakPieces predicate text)
160160
in case pieces of
161161
[] -> ("", "")

core-text/core-text.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cabal-version: 1.18
55
-- see: https://github.com/sol/hpack
66

77
name: core-text
8-
version: 0.3.6.0
8+
version: 0.3.7.0
99
synopsis: A rope type based on a finger tree over UTF-8 fragments
1010
description: A rope data type for text, built as a finger tree over UTF-8 text
1111
fragments. The package also includes utiltiy functions for breaking and

core-text/lib/Core/Text/Breaking.hs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
-- This is an Internal module, hidden from Haddock
55
module Core.Text.Breaking (
6+
breakRope,
67
breakWords,
78
breakLines,
89
breakPieces,
@@ -142,3 +143,24 @@ intoChunks predicate piece =
142143
in if trailing
143144
then intoRope chunk : emptyRope : []
144145
else intoRope chunk : intoChunks predicate remainder'
146+
147+
{-
148+
The utilities breakPieces and its helpers above were written long before this
149+
code. The special purpose functions above might have been written more easily
150+
if this below had been written first, but they _are_ special cases and they're
151+
done, so {shrug} if someone wants to unify these go right head, otherwise this
152+
can stand as almost but not-quite repetition.
153+
-}
154+
155+
{- |
156+
Given a piece of 'Rope' and a predicate, break the text into two pieces at the first
157+
site where that predicate returns 'True'.
158+
159+
@since 0.3.7
160+
-}
161+
breakRope :: (Char -> Bool) -> Rope -> (Rope, Rope)
162+
breakRope predicate text =
163+
let possibleIndex = findIndexRope predicate text
164+
in case possibleIndex of
165+
Nothing -> (text, emptyRope)
166+
Just i -> splitRope i text

core-text/lib/Core/Text/Rope.hs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ module Core.Text.Rope (
7979
replicateRope,
8080
replicateChar,
8181
widthRope,
82+
unconsRope,
8283
splitRope,
8384
takeRope,
8485
insertRope,
@@ -154,7 +155,7 @@ import qualified Data.Text.Short as S (
154155
splitAt,
155156
toBuilder,
156157
toText,
157-
unpack,
158+
unpack, uncons
158159
)
159160
import qualified Data.Text.Short.Unsafe as S (fromByteStringUnsafe)
160161
import GHC.Generics (Generic)
@@ -332,6 +333,24 @@ nullRope (Rope x) = case F.viewl x of
332333
F.EmptyL -> True
333334
(F.:<) piece _ -> S.null piece
334335

336+
{- |
337+
Read the first character from a 'Rope', assuming it's length 1 or greater,
338+
returning 'Just' that character and the remainder of the text. Returns
339+
'Nothing' if the input is 0 length.
340+
341+
@since 0.3.7
342+
-}
343+
unconsRope :: Rope -> Maybe (Char, Rope)
344+
unconsRope text =
345+
let x = unRope text
346+
in case F.viewl x of
347+
F.EmptyL -> Nothing
348+
(F.:<) piece x' ->
349+
case S.uncons piece of
350+
Nothing -> Nothing
351+
Just (c, piece') -> Just (c, Rope ((F.<|) piece' x'))
352+
353+
335354
{- |
336355
Break the text into two pieces at the specified offset.
337356

core-text/lib/Core/Text/Utilities.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module Core.Text.Utilities (
2121
-- * Helpers
2222
indefinite,
2323
oxford,
24+
breakRope,
2425
breakWords,
2526
breakLines,
2627
breakPieces,

core-text/package.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: core-text
2-
version: 0.3.6.0
2+
version: 0.3.7.0
33
synopsis: A rope type based on a finger tree over UTF-8 fragments
44
description: |
55
A rope data type for text, built as a finger tree over UTF-8 text

0 commit comments

Comments
 (0)