Improved object serialization

This commit is contained in:
mdecimus
2025-02-23 12:22:11 +01:00
parent 4b3b158ed3
commit b7c0f8447b
118 changed files with 4666 additions and 3165 deletions

View File

@@ -0,0 +1,26 @@
pub mod parser;
pub mod pointer;
use downcast_rs::{Downcast, impl_downcast};
use std::{fmt::Debug, slice::Iter};
pub trait JsonQueryable: Downcast + Debug + 'static {
fn eval_pointer<'x>(
&'x self,
pointer: Iter<JsonPointerItem>,
results: &mut Vec<&'x dyn JsonQueryable>,
);
}
impl_downcast!(JsonQueryable);
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
pub struct JsonPointer(pub Vec<JsonPointerItem>);
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
pub enum JsonPointerItem {
Root,
Wildcard,
String(String),
Number(u64),
}

View File

@@ -0,0 +1,65 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::codec::{base32_custom::BASE32_INVERSE, leb128::Leb128Iterator};
use super::json::Parser;
#[derive(Debug)]
pub struct JsonBase32Reader<'x, 'y> {
bytes: &'y mut Parser<'x>,
last_byte: u8,
pos: usize,
}
impl<'x, 'y> JsonBase32Reader<'x, 'y> {
pub fn new(bytes: &'y mut Parser<'x>) -> Self {
JsonBase32Reader {
bytes,
pos: 0,
last_byte: 0,
}
}
#[inline(always)]
fn map_byte(&mut self) -> Option<u8> {
match self.bytes.next_unescaped() {
Ok(Some(byte)) => match BASE32_INVERSE[byte as usize] {
decoded_byte if decoded_byte != u8::MAX => {
self.last_byte = decoded_byte;
Some(decoded_byte)
}
_ => None,
},
_ => None,
}
}
pub fn error(&mut self) -> trc::Error {
self.bytes.error_value()
}
}
impl Iterator for JsonBase32Reader<'_, '_> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
let pos = self.pos % 5;
let last_byte = self.last_byte;
let byte = self.map_byte()?;
self.pos += 1;
match pos {
0 => ((byte << 3) | (self.map_byte().unwrap_or(0) >> 2)).into(),
1 => ((last_byte << 6) | (byte << 1) | (self.map_byte().unwrap_or(0) >> 4)).into(),
2 => ((last_byte << 4) | (byte >> 1)).into(),
3 => ((last_byte << 7) | (byte << 2) | (self.map_byte().unwrap_or(0) >> 3)).into(),
4 => ((last_byte << 5) | byte).into(),
_ => None,
}
}
}
impl Leb128Iterator<u8> for JsonBase32Reader<'_, '_> {}

View File

@@ -0,0 +1,308 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::fmt::Display;
use crate::map::{
bitmap::{Bitmap, BitmapItem},
vec_map::VecMap,
};
use super::{Ignore, JsonObjectParser, Token, json::Parser};
impl JsonObjectParser for u64 {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let mut hash = 0;
let mut shift = 0;
while let Some(ch) = parser.next_unescaped()? {
if shift < 64 {
hash |= (ch as u64) << shift;
shift += 8;
} else {
hash = 0;
break;
}
}
Ok(hash)
}
}
impl JsonObjectParser for u128 {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let mut hash = 0;
let mut shift = 0;
while let Some(ch) = parser.next_unescaped()? {
if shift < 128 {
hash |= (ch as u128) << shift;
shift += 8;
} else {
hash = 0;
break;
}
}
Ok(hash)
}
}
impl JsonObjectParser for String {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let start_pos = parser.pos;
while let Some(ch) = parser.next_char() {
match ch {
b'\\' => {
let mut is_escaped = true;
let mut buf = Vec::with_capacity((parser.pos - start_pos) + 16);
buf.extend_from_slice(&parser.bytes[start_pos..parser.pos - 1]);
while let Some(ch) = parser.next_char() {
match ch {
b'\\' if !is_escaped => {
is_escaped = true;
}
b'"' if !is_escaped => {
parser.is_eof = true;
return String::from_utf8(buf).map_err(|_| parser.error_utf8());
}
_ => {
if !is_escaped {
buf.push(ch);
} else {
match ch {
b'"' => {
buf.push(b'"');
}
b'\\' => {
buf.push(b'\\');
}
b'n' => {
buf.push(b'\n');
}
b't' => {
buf.push(b'\t');
}
b'r' => {
buf.push(b'\r');
}
b'b' => {
buf.push(0x08);
}
b'f' => {
buf.push(0x0c);
}
b'/' => {
buf.push(b'/');
}
b'u' => {
let mut code = [
*parser.iter.next().ok_or_else(|| {
parser.error("Incomplete unicode sequence")
})?,
*parser.iter.next().ok_or_else(|| {
parser.error("Incomplete unicode sequence")
})?,
*parser.iter.next().ok_or_else(|| {
parser.error("Incomplete unicode sequence")
})?,
*parser.iter.next().ok_or_else(|| {
parser.error("Incomplete unicode sequence")
})?,
];
parser.pos += 4;
let code_str = std::str::from_utf8(&code)
.map_err(|_| parser.error_utf8())?;
let code_str = char::from_u32(
u32::from_str_radix(code_str, 16).map_err(
|_| {
parser.error(&format!(
"Invalid unicode sequence {code_str}"
))
},
)?,
)
.ok_or_else(|| {
parser.error(&format!(
"Invalid unicode sequence {code_str}"
))
})?
.encode_utf8(&mut code);
buf.extend_from_slice(code_str.as_bytes());
}
_ => {
buf.push(ch);
}
}
is_escaped = false;
}
}
}
}
break;
}
b'"' => {
parser.is_eof = true;
return std::str::from_utf8(
parser
.bytes
.get(start_pos..parser.pos - 1)
.unwrap_or_default(),
)
.map(Into::into)
.map_err(|_| parser.error_utf8());
}
_ => (),
}
}
Err(parser.error_unterminated())
}
}
impl<T: JsonObjectParser + Eq> JsonObjectParser for Vec<T> {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let mut vec = Vec::new();
parser.next_token::<Ignore>()?.assert(Token::ArrayStart)?;
loop {
match parser.next_token::<T>()? {
Token::String(item) => vec.push(item),
Token::Comma => (),
Token::ArrayEnd => break,
token => return Err(token.error("", "[ or string")),
}
}
Ok(vec)
}
}
impl<T: JsonObjectParser + Eq> JsonObjectParser for Option<Vec<T>> {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
match parser.next_token::<Ignore>()? {
Token::ArrayStart => {
let mut vec = Vec::new();
loop {
match parser.next_token::<T>()? {
Token::String(item) => vec.push(item),
Token::Comma => (),
Token::ArrayEnd => break,
token => return Err(token.error("", "string")),
}
}
Ok(Some(vec))
}
Token::Null => Ok(None),
token => Err(token.error("", "array or null")),
}
}
}
impl<T: JsonObjectParser + Eq + BitmapItem> JsonObjectParser for Bitmap<T> {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let mut bm = Bitmap::new();
match parser.next_token::<Ignore>()? {
Token::ArrayStart => {
loop {
match parser.next_token::<T>()? {
Token::String(item) => bm.insert(item),
Token::Comma => (),
Token::ArrayEnd => break,
token => return Err(token.error("", "string")),
}
}
Ok(bm)
}
Token::Null => Ok(bm),
token => Err(token.error("", "array or null")),
}
}
}
impl<K: JsonObjectParser + Eq + Display, V: JsonObjectParser> JsonObjectParser for VecMap<K, V> {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let mut map = VecMap::new();
parser.next_token::<Ignore>()?.assert(Token::DictStart)?;
while let Some(key) = parser.next_dict_key()? {
map.append(key, V::parse(parser)?);
}
Ok(map)
}
}
impl<K: JsonObjectParser + Eq + Display, V: JsonObjectParser> JsonObjectParser
for Option<VecMap<K, V>>
{
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
match parser.next_token::<Ignore>()? {
Token::DictStart => {
let mut map = VecMap::new();
while let Some(key) = parser.next_dict_key()? {
map.append(key, V::parse(parser)?);
}
Ok(Some(map))
}
Token::Null => Ok(None),
token => Err(token.error("", &token.to_string())),
}
}
}
impl JsonObjectParser for bool {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
match parser.next_token::<Ignore>()? {
Token::Boolean(value) => Ok(value),
Token::Null => Ok(false),
token => Err(token.error("", &token.to_string())),
}
}
}
impl JsonObjectParser for Ignore {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
if parser.skip_string() {
Ok(Ignore {})
} else {
Err(parser.error_unterminated())
}
}
}

View File

@@ -0,0 +1,386 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::{fmt::Display, iter::Peekable, slice::Iter};
use super::{Ignore, JsonObjectParser, Token};
const MAX_NESTED_LEVELS: u32 = 16;
#[derive(Debug)]
pub struct Parser<'x> {
pub bytes: &'x [u8],
pub iter: Peekable<Iter<'x, u8>>,
pub next_ch: Option<u8>,
pub pos: usize,
pub pos_marker: usize,
pub depth_array: u32,
pub depth_dict: u32,
pub is_eof: bool,
}
impl<'x> Parser<'x> {
pub fn new(bytes: &'x [u8]) -> Self {
Self {
bytes,
iter: bytes.iter().peekable(),
next_ch: None,
pos: 0,
pos_marker: 0,
is_eof: false,
depth_array: 0,
depth_dict: 0,
}
}
pub fn error(&self, message: &str) -> trc::Error {
trc::JmapEvent::NotJson
.into_err()
.details(format!("{message} at position {}.", self.pos))
}
pub fn error_unterminated(&self) -> trc::Error {
trc::JmapEvent::NotJson.into_err().details(format!(
"Unterminated string at position {pos}.",
pos = self.pos
))
}
pub fn error_utf8(&self) -> trc::Error {
trc::JmapEvent::NotJson.into_err().details(format!(
"Invalid UTF-8 sequence at position {pos}.",
pos = self.pos
))
}
pub fn error_value(&mut self) -> trc::Error {
if self.is_eof || self.skip_string() {
trc::JmapEvent::InvalidArguments.into_err().details(format!(
"Invalid value {:?} at position {}.",
String::from_utf8_lossy(self.bytes[self.pos_marker..self.pos - 1].as_ref()),
self.pos
))
} else {
self.error_unterminated()
}
}
#[inline(always)]
pub fn peek_char(&mut self) -> Option<u8> {
self.iter.peek().map(|&&ch| ch)
}
#[inline(always)]
pub fn next_char(&mut self) -> Option<u8> {
self.pos += 1;
self.iter.next().copied()
}
#[inline(always)]
pub fn next_unescaped(&mut self) -> trc::Result<Option<u8>> {
match self.next_char() {
Some(b'"') => {
self.is_eof = true;
Ok(None)
}
Some(b'\\') => self
.next_char()
.ok_or_else(|| self.error_unterminated())
.map(Some),
Some(ch) => Ok(Some(ch)),
None => {
if self.is_eof {
Ok(None)
} else {
Err(self.error_unterminated())
}
}
}
}
pub fn skip_string(&mut self) -> bool {
let mut last_ch = 0;
while let Some(ch) = self.next_char() {
if ch == b'"' && last_ch != b'\\' {
self.is_eof = true;
return true;
} else {
last_ch = ch;
}
}
false
}
pub fn next_token<T: JsonObjectParser>(&mut self) -> trc::Result<Token<T>> {
let mut next_ch = self.next_ch.take().or_else(|| self.next_char());
while let Some(mut ch) = next_ch {
match ch {
b'"' => {
self.pos_marker = self.pos;
self.is_eof = false;
let value = T::parse(self)?;
return if self.is_eof || self.skip_string() {
Ok(Token::String(value))
} else {
Err(self.error_unterminated())
};
}
b',' => {
return Ok(Token::Comma);
}
b':' => {
return Ok(Token::Colon);
}
b'[' => {
if self.depth_array + self.depth_dict < MAX_NESTED_LEVELS {
self.depth_array += 1;
return Ok(Token::ArrayStart);
} else {
return Err(self.error("Too many nested objects"));
}
}
b']' => {
return if self.depth_array != 0 {
self.depth_array -= 1;
Ok(Token::ArrayEnd)
} else {
Err(self.error("Unexpected array end"))
};
}
b'{' => {
if self.depth_array + self.depth_dict < MAX_NESTED_LEVELS {
self.depth_dict += 1;
return Ok(Token::DictStart);
} else {
return Err(self.error("Too many nested objects"));
}
}
b'}' => {
return if self.depth_dict != 0 {
self.depth_dict -= 1;
Ok(Token::DictEnd)
} else {
Err(self.error("Unexpected dictionary end"))
};
}
b'0'..=b'9' | b'-' | b'+' => {
let mut num: i64 = 0;
let mut is_float = false;
let mut is_negative = false;
let num_start = self.pos - 1;
loop {
match ch {
b'-' => {
is_negative = true;
}
b'0'..=b'9' => {
if !is_float {
num = num.saturating_mul(10).saturating_add((ch - b'0') as i64);
}
}
b',' | b']' | b'}' => {
self.next_ch = ch.into();
break;
}
b'+' => (),
b'.' | b'e' | b'E' => {
is_float = true;
}
b' ' | b'\r' | b'\t' | b'\n' => {
break;
}
_ => {
return Err(self
.error(&format!("Unexpected character {:?}", char::from(ch))));
}
}
ch = self.next_char().ok_or_else(|| self.error_unterminated())?;
}
return if !is_float {
Ok(Token::Integer(if !is_negative { num } else { -num }))
} else {
fast_float::parse(
self.bytes.get(num_start..self.pos - 1).unwrap_or_default(),
)
.map(Token::Float)
.map_err(|_| {
self.error(&format!(
"Failed to parse number {:?}",
String::from_utf8_lossy(
self.bytes.get(num_start..self.pos - 1).unwrap_or_default()
)
))
})
};
}
b't' => {
return if let (Some(b'r'), Some(b'u'), Some(b'e')) =
(self.iter.next(), self.iter.next(), self.iter.next())
{
self.pos += 3;
Ok(Token::Boolean(true))
} else {
Err(self.error("Invalid JSON token"))
};
}
b'f' => {
return if let (Some(b'a'), Some(b'l'), Some(b's'), Some(b'e')) = (
self.iter.next(),
self.iter.next(),
self.iter.next(),
self.iter.next(),
) {
self.pos += 4;
Ok(Token::Boolean(false))
} else {
Err(self.error("Invalid JSON token"))
};
}
b'n' => {
return if let (Some(b'u'), Some(b'l'), Some(b'l')) =
(self.iter.next(), self.iter.next(), self.iter.next())
{
self.pos += 3;
Ok(Token::Null)
} else {
Err(self.error("Invalid JSON token"))
};
}
b' ' | b'\t' | b'\r' | b'\n' => (),
_ => {
return Err(self.error(&format!("Unexpected character {:?}", char::from(ch))));
}
}
next_ch = self.next_char();
}
Err(self.error("Unexpected EOF"))
}
pub fn next_dict_key<T: JsonObjectParser + Display + Eq>(&mut self) -> trc::Result<Option<T>> {
loop {
match self.next_token::<T>()? {
Token::String(k) => {
self.next_token::<T>()?.assert(Token::Colon)?;
return Ok(Some(k));
}
Token::Comma => (),
Token::DictEnd => return Ok(None),
token => {
return Err(self.error(&format!("Expected object property, found {}", token)));
}
}
}
}
pub fn skip_token(&mut self, start_depth_array: u32, start_depth_dict: u32) -> trc::Result<()> {
while {
self.next_token::<Ignore>()?;
start_depth_array != self.depth_array || start_depth_dict != self.depth_dict
} {}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::json::parser::Token;
use super::Parser;
#[test]
fn parse_json() {
for (input, expected_result) in [
(
&b"[true, false, 123, 456 , -123, 0.123, -0.456, 3.7e-5, 6.02e+23, null]"[..],
vec![
Token::ArrayStart,
Token::Boolean(true),
Token::Comma,
Token::Boolean(false),
Token::Comma,
Token::Integer(123),
Token::Comma,
Token::Integer(456),
Token::Comma,
Token::Integer(-123),
Token::Comma,
Token::Float(0.123),
Token::Comma,
Token::Float(-0.456),
Token::Comma,
Token::Float(3.7e-5),
Token::Comma,
Token::Float(6.02e23),
Token::Comma,
Token::Null,
Token::ArrayEnd,
],
),
(
&b"{\"\": true, \"\": false , \"\": {\"\": 123}, \"\": [ ]}"[..],
vec![
Token::DictStart,
Token::String("".to_string()),
Token::Colon,
Token::Boolean(true),
Token::Comma,
Token::String("".to_string()),
Token::Colon,
Token::Boolean(false),
Token::Comma,
Token::String("".to_string()),
Token::Colon,
Token::DictStart,
Token::String("".to_string()),
Token::Colon,
Token::Integer(123),
Token::DictEnd,
Token::Comma,
Token::String("".to_string()),
Token::Colon,
Token::ArrayStart,
Token::ArrayEnd,
Token::DictEnd,
],
),
] {
let mut p = Parser::new(input);
let mut result = Vec::new();
while let Ok(token) = p.next_token() {
result.push(token);
}
assert_eq!(result, expected_result);
}
for (input, expected_result) in [
("hello\t\nworld", "hello\t\nworld"),
("hello\t\n\\\"world\\\"\\n", "hello\t\n\"world\"\n"),
("\\\"hello\\\tworld\\\"", "\"hello\tworld\""),
("\\u0009\\u0020\\u263A", "\t"),
("", ""),
] {
assert_eq!(
Parser::new(format!("\"{input}\"").as_bytes())
.next_token::<String>()
.unwrap()
.unwrap_string("")
.unwrap(),
expected_result
);
}
}
}

View File

@@ -0,0 +1,158 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::fmt::Display;
use self::json::Parser;
pub mod base32;
pub mod impls;
pub mod json;
pub mod pointer;
#[derive(Debug, PartialEq, Clone)]
pub enum Token<T> {
Colon,
Comma,
DictStart,
DictEnd,
ArrayStart,
ArrayEnd,
Integer(i64),
Float(f64),
Boolean(bool),
String(T),
Null,
}
impl<T: PartialEq> Eq for Token<T> {}
pub trait JsonObjectParser {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ignore {}
impl<T: Eq> Token<T> {
pub fn unwrap_string(self, property: &str) -> trc::Result<T> {
match self {
Token::String(s) => Ok(s),
token => Err(token.error(property, "string")),
}
}
pub fn unwrap_string_or_null(self, property: &str) -> trc::Result<Option<T>> {
match self {
Token::String(s) => Ok(Some(s)),
Token::Null => Ok(None),
token => Err(token.error(property, "string")),
}
}
pub fn unwrap_bool(self, property: &str) -> trc::Result<bool> {
match self {
Token::Boolean(v) => Ok(v),
token => Err(token.error(property, "boolean")),
}
}
pub fn unwrap_bool_or_null(self, property: &str) -> trc::Result<Option<bool>> {
match self {
Token::Boolean(v) => Ok(Some(v)),
Token::Null => Ok(None),
token => Err(token.error(property, "boolean")),
}
}
pub fn unwrap_usize_or_null(self, property: &str) -> trc::Result<Option<usize>> {
match self {
Token::Integer(v) if v >= 0 => Ok(Some(v as usize)),
Token::Float(v) if v >= 0.0 => Ok(Some(v as usize)),
Token::Null => Ok(None),
token => Err(token.error(property, "unsigned integer")),
}
}
pub fn unwrap_uint_or_null(self, property: &str) -> trc::Result<Option<u64>> {
match self {
Token::Integer(v) if v >= 0 => Ok(Some(v as u64)),
Token::Float(v) if v >= 0.0 => Ok(Some(v as u64)),
Token::Null => Ok(None),
token => Err(token.error(property, "unsigned integer")),
}
}
pub fn unwrap_int_or_null(self, property: &str) -> trc::Result<Option<i64>> {
match self {
Token::Integer(v) => Ok(Some(v)),
Token::Float(v) => Ok(Some(v as i64)),
Token::Null => Ok(None),
token => Err(token.error(property, "unsigned integer")),
}
}
pub fn unwrap_ints_or_null(self, property: &str) -> trc::Result<Option<i32>> {
match self {
Token::Integer(v) => Ok(Some(v as i32)),
Token::Float(v) => Ok(Some(v as i32)),
Token::Null => Ok(None),
token => Err(token.error(property, "unsigned integer")),
}
}
pub fn assert(self, token: Token<T>) -> trc::Result<()> {
if self == token {
Ok(())
} else {
Err(self.error("", &token.to_string()))
}
}
pub fn assert_jmap(self, token: Token<T>) -> trc::Result<()> {
if self == token {
Ok(())
} else {
Err(trc::JmapEvent::NotRequest.into_err().details(format!(
"Invalid JMAP request: expected '{token}', got '{self}'."
)))
}
}
pub fn error(&self, property: &str, expected: &str) -> trc::Error {
trc::JmapEvent::InvalidArguments.into_err().details(if !property.is_empty() {
format!("Invalid argument for '{property:?}': expected '{expected}', got '{self}'.",)
} else {
format!("Invalid argument: expected '{expected}', got '{self}'.")
})
}
}
impl Display for Ignore {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "string")
}
}
impl<T> Display for Token<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Token::Colon => write!(f, ":"),
Token::Comma => write!(f, ","),
Token::DictStart => write!(f, "{{"),
Token::DictEnd => write!(f, "}}"),
Token::ArrayStart => write!(f, "["),
Token::ArrayEnd => write!(f, "]"),
Token::Integer(i) => write!(f, "{}", i),
Token::Float(v) => write!(f, "{}", v),
Token::Boolean(b) => write!(f, "{}", b),
Token::Null => write!(f, "null"),
Token::String(_) => write!(f, "string"),
}
}
}

View File

@@ -0,0 +1,222 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::fmt::Display;
use crate::json::{JsonPointer, JsonPointerItem};
use super::{JsonObjectParser, json::Parser};
enum TokenType {
Unknown,
Number,
String,
Wildcard,
Escaped,
}
impl JsonObjectParser for JsonPointer {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let mut path = Vec::new();
let mut num = 0u64;
let mut buf = Vec::new();
let mut token = TokenType::Unknown;
let mut start_pos = parser.pos;
while let Some(ch) = parser.next_char() {
match (ch, &token) {
(b'0'..=b'9', TokenType::Unknown | TokenType::Number) => {
num = num.saturating_mul(10).saturating_add((ch - b'0') as u64);
token = TokenType::Number;
}
(b'*', TokenType::Unknown) => {
token = TokenType::Wildcard;
}
(b'0', TokenType::Escaped) => {
buf.push(b'~');
token = TokenType::String;
}
(b'1', TokenType::Escaped) => {
buf.push(b'/');
token = TokenType::String;
}
(b'/' | b'"', _) => {
match token {
TokenType::String => {
path.push(JsonPointerItem::String(
String::from_utf8(buf).map_err(|_| parser.error_utf8())?,
));
buf = Vec::new();
}
TokenType::Number => {
path.push(JsonPointerItem::Number(num));
num = 0;
}
TokenType::Wildcard => {
path.push(JsonPointerItem::Wildcard);
}
TokenType::Unknown if parser.pos_marker != start_pos => {
path.push(JsonPointerItem::String(String::new()));
}
_ => (),
}
if ch == b'/' {
token = TokenType::Unknown;
start_pos = parser.pos;
} else {
parser.is_eof = true;
if path.is_empty() {
path.push(JsonPointerItem::Root);
}
return Ok(JsonPointer(path));
}
}
(_, _) => {
if matches!(&token, TokenType::Number | TokenType::Wildcard)
&& parser.pos - 1 > start_pos
{
buf.extend_from_slice(
parser
.bytes
.get(start_pos..parser.pos - 1)
.unwrap_or_default(),
);
}
token = match ch {
b'~' if !matches!(&token, TokenType::Escaped) => TokenType::Escaped,
b'\\' => {
buf.push(parser.next_char().unwrap_or(b'\\'));
TokenType::String
}
_ => {
buf.push(ch);
TokenType::String
}
};
}
}
}
Err(parser.error_unterminated())
}
}
impl Display for JsonPointer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (i, ptr) in self.0.iter().enumerate() {
if i > 0 {
write!(f, "/")?;
}
write!(f, "{}", ptr)?;
}
Ok(())
}
}
impl Display for JsonPointerItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
JsonPointerItem::Root => write!(f, "/"),
JsonPointerItem::Wildcard => write!(f, "*"),
JsonPointerItem::String(s) => write!(f, "{}", s),
JsonPointerItem::Number(n) => write!(f, "{}", n),
}
}
}
#[cfg(test)]
mod tests {
use crate::json::parser::json::Parser;
use super::{JsonPointer, JsonPointerItem};
#[test]
fn json_pointer_parse() {
for (input, output) in vec![
("hello", vec![JsonPointerItem::String("hello".to_string())]),
("9a", vec![JsonPointerItem::String("9a".to_string())]),
("a9", vec![JsonPointerItem::String("a9".to_string())]),
("*a", vec![JsonPointerItem::String("*a".to_string())]),
(
"/hello/world",
vec![
JsonPointerItem::String("hello".to_string()),
JsonPointerItem::String("world".to_string()),
],
),
("*", vec![JsonPointerItem::Wildcard]),
(
"/hello/*",
vec![
JsonPointerItem::String("hello".to_string()),
JsonPointerItem::Wildcard,
],
),
("1234", vec![JsonPointerItem::Number(1234)]),
(
"/hello/1234",
vec![
JsonPointerItem::String("hello".to_string()),
JsonPointerItem::Number(1234),
],
),
("~0~1", vec![JsonPointerItem::String("~/".to_string())]),
(
"/hello/~0~1",
vec![
JsonPointerItem::String("hello".to_string()),
JsonPointerItem::String("~/".to_string()),
],
),
(
"/hello/1~0~1/*~1~0",
vec![
JsonPointerItem::String("hello".to_string()),
JsonPointerItem::String("1~/".to_string()),
JsonPointerItem::String("*/~".to_string()),
],
),
(
"/hello/world/*/99",
vec![
JsonPointerItem::String("hello".to_string()),
JsonPointerItem::String("world".to_string()),
JsonPointerItem::Wildcard,
JsonPointerItem::Number(99),
],
),
("/", vec![JsonPointerItem::String("".to_string())]),
(
"///",
vec![
JsonPointerItem::String("".to_string()),
JsonPointerItem::String("".to_string()),
JsonPointerItem::String("".to_string()),
],
),
("", vec![JsonPointerItem::Root]),
] {
assert_eq!(
Parser::new(format!("\"{input}\"").as_bytes())
.next_token::<JsonPointer>()
.unwrap()
.unwrap_string("")
.unwrap()
.0,
output,
"{input}"
);
}
}
}

View File

@@ -0,0 +1,106 @@
use super::{JsonPointerItem, JsonQueryable};
use std::hash::BuildHasher;
use std::{collections::HashMap, slice::Iter};
impl<T: JsonQueryable> JsonQueryable for Vec<T> {
fn eval_pointer<'x>(
&'x self,
mut pointer: Iter<JsonPointerItem>,
results: &mut Vec<&'x dyn JsonQueryable>,
) {
match pointer.next() {
Some(JsonPointerItem::Number(n)) => {
if let Some(v) = self.get(*n as usize) {
v.eval_pointer(pointer, results);
}
}
Some(JsonPointerItem::Wildcard) => {
for v in self {
v.eval_pointer(pointer.clone(), results);
}
}
Some(JsonPointerItem::Root) | None => {
results.push(self);
}
_ => {}
}
}
}
impl<V: JsonQueryable, S: BuildHasher + Default + 'static> JsonQueryable for HashMap<String, V, S> {
fn eval_pointer<'x>(
&'x self,
mut pointer: Iter<JsonPointerItem>,
results: &mut Vec<&'x dyn JsonQueryable>,
) {
match pointer.next() {
Some(JsonPointerItem::String(n)) => {
if let Some(v) = self.get(n) {
v.eval_pointer(pointer, results);
}
}
Some(JsonPointerItem::Number(n)) => {
let n = n.to_string();
if let Some(v) = self.get(&n) {
v.eval_pointer(pointer, results);
}
}
Some(JsonPointerItem::Wildcard) => {
for v in self.values() {
v.eval_pointer(pointer.clone(), results);
}
}
Some(JsonPointerItem::Root) | None => {
results.push(self);
}
}
}
}
impl JsonQueryable for serde_json::Value {
fn eval_pointer<'x>(
&'x self,
mut pointer: Iter<JsonPointerItem>,
results: &mut Vec<&'x dyn JsonQueryable>,
) {
match pointer.next() {
Some(JsonPointerItem::String(n)) => {
if let serde_json::Value::Object(map) = self {
if let Some(v) = map.get(n) {
v.eval_pointer(pointer, results);
}
}
}
Some(JsonPointerItem::Number(n)) => match self {
serde_json::Value::Array(values) => {
if let Some(v) = values.get(*n as usize) {
v.eval_pointer(pointer, results);
}
}
serde_json::Value::Object(map) => {
let n = n.to_string();
if let Some(v) = map.get(&n) {
v.eval_pointer(pointer, results);
}
}
_ => {}
},
Some(JsonPointerItem::Wildcard) => match self {
serde_json::Value::Array(values) => {
for v in values {
v.eval_pointer(pointer.clone(), results);
}
}
serde_json::Value::Object(map) => {
for v in map.values() {
v.eval_pointer(pointer.clone(), results);
}
}
_ => {}
},
Some(JsonPointerItem::Root) | None => {
results.push(self);
}
}
}
}

View File

@@ -10,6 +10,7 @@ pub mod cache;
pub mod codec;
pub mod config;
pub mod glob;
pub mod json;
pub mod map;
pub mod snowflake;
pub mod url_params;
@@ -17,11 +18,14 @@ pub mod url_params;
use futures::StreamExt;
use reqwest::Response;
use rustls::{
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
ClientConfig, RootCertStore, SignatureScheme,
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
};
use rustls_pki_types::TrustAnchor;
pub use downcast_rs;
pub use erased_serde;
pub const BLOB_HASH_LEN: usize = 32;
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
@@ -247,7 +251,7 @@ pub fn failed(message: &str) -> ! {
pub async fn wait_for_shutdown() {
#[cfg(not(target_env = "msvc"))]
let signal = {
use tokio::signal::unix::{signal, SignalKind};
use tokio::signal::unix::{SignalKind, signal};
let mut h_term = signal(SignalKind::terminate()).failed("start signal handler");
let mut h_int = signal(SignalKind::interrupt()).failed("start signal handler");

View File

@@ -6,7 +6,7 @@
use std::{borrow::Borrow, cmp::Ordering, fmt, hash::Hash};
use serde::{de::DeserializeOwned, ser::SerializeMap, Deserialize, Serialize};
use serde::{Deserialize, Serialize, de::DeserializeOwned, ser::SerializeMap};
// A map implemented using vectors
// used for small datasets of less than 20 items
@@ -56,6 +56,12 @@ impl<K: Eq + PartialEq, V> VecMap<K, V> {
self.inner.push(KeyValue { key, value });
}
#[inline(always)]
pub fn with_append(mut self, key: K, value: V) -> Self {
self.append(key, value);
self
}
#[inline(always)]
pub fn insert(&mut self, idx: usize, key: K, value: V) {
self.inner.insert(idx, KeyValue { key, value });