{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
module Json.Path
( Path (..)
, encode
, builderUtf8
, query
, query'
, reverse
) where
import Prelude hiding (reverse)
import Data.ByteString.Short.Internal (ShortByteString (SBS))
import Data.Bytes.Builder (Builder)
import Data.Primitive (ByteArray (ByteArray))
import Data.Text.Short (ShortText)
import Json (Member (Member), Value (Array, Null, Object))
import qualified Data.Bytes.Builder as Builder
import qualified Data.Bytes.Chunks as ByteChunks
import qualified Data.Primitive as PM
import qualified Data.Text.Short.Unsafe as TS
data Path
=
Key {-# UNPACK #-} !ShortText !Path
|
Index {-# UNPACK #-} !Int !Path
| Nil
deriving (Path -> Path -> Bool
(Path -> Path -> Bool) -> (Path -> Path -> Bool) -> Eq Path
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Path -> Path -> Bool
== :: Path -> Path -> Bool
$c/= :: Path -> Path -> Bool
/= :: Path -> Path -> Bool
Eq, Int -> Path -> ShowS
[Path] -> ShowS
Path -> String
(Int -> Path -> ShowS)
-> (Path -> String) -> ([Path] -> ShowS) -> Show Path
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Path -> ShowS
showsPrec :: Int -> Path -> ShowS
$cshow :: Path -> String
show :: Path -> String
$cshowList :: [Path] -> ShowS
showList :: [Path] -> ShowS
Show)
encode :: Path -> ShortText
encode :: Path -> ShortText
encode Path
p = ByteArray -> ShortText
ba2st (Chunks -> ByteArray
ByteChunks.concatU (Int -> Builder -> Chunks
Builder.run Int
128 (Path -> Builder
builderUtf8 Path
p)))
builderUtf8 :: Path -> Builder
builderUtf8 :: Path -> Builder
builderUtf8 Path
p0 = Char -> Builder
Builder.ascii Char
'$' Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Path -> Builder
go Path
p0
where
go :: Path -> Builder
go Path
Nil = Builder
forall a. Monoid a => a
mempty
go (Key ShortText
k Path
p) = Char -> Builder
Builder.ascii Char
'.' Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ShortText -> Builder
Builder.shortTextUtf8 ShortText
k Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Path -> Builder
go Path
p
go (Index Int
i Path
p) =
Char -> Builder
Builder.ascii Char
'['
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word -> Builder
Builder.wordDec (Int -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
i)
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.ascii Char
']'
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Path -> Builder
go Path
p
query :: Path -> Value -> Maybe Value
query :: Path -> Value -> Maybe Value
query = Path -> Value -> Maybe Value
go
where
go :: Path -> Value -> Maybe Value
go Path
Nil Value
v = Value -> Maybe Value
forall a. a -> Maybe a
Just Value
v
go (Key ShortText
k Path
p) (Object SmallArray Member
mbrs) =
(Member -> Maybe Value -> Maybe Value)
-> Maybe Value -> SmallArray Member -> Maybe Value
forall a b. (a -> b -> b) -> b -> SmallArray a -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr
( \(Member ShortText
key Value
val) Maybe Value
other ->
if ShortText
key ShortText -> ShortText -> Bool
forall a. Eq a => a -> a -> Bool
== ShortText
k
then Value -> Maybe Value
forall a. a -> Maybe a
Just Value
val
else Maybe Value
other
)
Maybe Value
forall a. Maybe a
Nothing
SmallArray Member
mbrs
Maybe Value -> (Value -> Maybe Value) -> Maybe Value
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Path -> Value -> Maybe Value
go Path
p
go (Index Int
i Path
p) (Array SmallArray Value
vs) =
if Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< SmallArray Value -> Int
forall a. SmallArray a -> Int
PM.sizeofSmallArray SmallArray Value
vs
then
let !(# Value
e #) = SmallArray Value -> Int -> (# Value #)
forall a. SmallArray a -> Int -> (# a #)
PM.indexSmallArray## SmallArray Value
vs Int
i
in Path -> Value -> Maybe Value
go Path
p Value
e
else Maybe Value
forall a. Maybe a
Nothing
go Path
_ Value
_ = Maybe Value
forall a. Maybe a
Nothing
query' :: Path -> Value -> Value
query' :: Path -> Value -> Value
query' = Path -> Value -> Value
go
where
go :: Path -> Value -> Value
go Path
Nil Value
v = Value
v
go (Key ShortText
k Path
p) (Object SmallArray Member
mbrs) =
Path -> Value -> Value
go Path
p (Value -> Value) -> Value -> Value
forall a b. (a -> b) -> a -> b
$
(Member -> Value -> Value) -> Value -> SmallArray Member -> Value
forall a b. (a -> b -> b) -> b -> SmallArray a -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr
( \(Member ShortText
key Value
val) Value
other ->
if ShortText
key ShortText -> ShortText -> Bool
forall a. Eq a => a -> a -> Bool
== ShortText
k
then Value
val
else Value
other
)
Value
Null
SmallArray Member
mbrs
go (Index Int
i Path
p) (Array SmallArray Value
vs) =
if Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< SmallArray Value -> Int
forall a. SmallArray a -> Int
PM.sizeofSmallArray SmallArray Value
vs
then
let !(# Value
e #) = SmallArray Value -> Int -> (# Value #)
forall a. SmallArray a -> Int -> (# a #)
PM.indexSmallArray## SmallArray Value
vs Int
i
in Path -> Value -> Value
go Path
p Value
e
else Value
Null
go Path
_ Value
_ = Value
Null
ba2st :: ByteArray -> ShortText
ba2st :: ByteArray -> ShortText
ba2st (ByteArray ByteArray#
x) = ShortByteString -> ShortText
TS.fromShortByteStringUnsafe (ByteArray# -> ShortByteString
SBS ByteArray#
x)
reverse :: Path -> Path
reverse :: Path -> Path
reverse = Path -> Path -> Path
go Path
Nil
where
go :: Path -> Path -> Path
go !Path
acc Path
Nil = Path
acc
go !Path
acc (Key ShortText
k Path
xs) = Path -> Path -> Path
go (ShortText -> Path -> Path
Key ShortText
k Path
acc) Path
xs
go !Path
acc (Index Int
i Path
xs) = Path -> Path -> Path
go (Int -> Path -> Path
Index Int
i Path
acc) Path
xs