#![allow(
clippy::question_mark,
reason = "? introduces extra code bloat slowing down compile times"
)]
#![allow(elided_lifetimes_in_paths)]
use std::ptr::NonNull;
#[doc(hidden)]
pub mod __internal;
pub mod binary;
mod byte_writer;
pub mod helper;
pub mod json;
mod lazy_parser;
pub mod parser;
mod rewriter;
mod strings;
pub mod text;
mod text_writer;
pub use rewriter::{PrettifyConfig, prettify};
mod third_party;
pub use byte_writer::BytesWriter;
use parser::JsonParentContext;
pub use text_writer::TextWriter;
pub mod error;
use binary::{Decoder, FromBinaryError};
use json::DecodeError;
use json::JsonValueKind;
use parser::Parser;
#[cfg(feature = "macros")]
pub use jsony_macros::array;
pub use byte_writer::IntoByteWriter;
#[cfg(feature = "macros")]
pub use jsony_macros::{Jsony, object};
pub use text_writer::IntoTextWriter;
pub unsafe trait FromBinary<'a>: Sized {
const POD: bool = false;
fn decode_binary(decoder: &mut Decoder<'a>) -> Self;
#[doc(hidden)]
#[cfg(not(target_endian = "little"))]
fn endian_transform(&mut self) {}
}
pub unsafe trait ToBinary {
const POD: bool = false;
fn encode_binary(&self, encoder: &mut BytesWriter);
#[doc(hidden)]
#[cfg(not(target_endian = "little"))]
fn endian_transform(&mut self) {}
}
pub unsafe trait FromJson<'a>: Sized + 'a {
#[inline]
unsafe fn emplace_from_json(
dest: NonNull<()>,
parser: &mut Parser<'a>,
) -> Result<(), &'static DecodeError> {
match Self::decode_json(parser) {
Ok(value) => {
unsafe {
dest.cast::<Self>().write(value);
}
Ok(())
}
Err(err) => Err(err),
}
}
#[inline]
fn decode_json(parser: &mut Parser<'a>) -> Result<Self, &'static DecodeError> {
let mut value = std::mem::MaybeUninit::<Self>::uninit();
if let Err(err) = unsafe {
Self::emplace_from_json(NonNull::new_unchecked(value.as_mut_ptr()).cast(), parser)
} {
Err(err)
} else {
Ok(unsafe { value.assume_init() })
}
}
}
mod __private {
pub trait Sealed {}
}
pub trait ToJson {
type Kind: JsonValueKind;
#[allow(non_snake_case)]
fn encode_json__jsony(&self, output: &mut TextWriter) -> Self::Kind;
}
#[repr(transparent)]
pub struct RawJson {
pub(crate) raw: str,
}
impl RawJson {
pub fn as_str(&self) -> &str {
&self.raw
}
}
impl RawJson {
pub(crate) fn new_unchecked(raw: &str) -> &RawJson {
if raw.is_empty() {
unsafe { &*("null" as *const str as *const RawJson) }
} else {
unsafe { &*(raw as *const str as *const RawJson) }
}
}
pub(crate) fn new_boxed_unchecked(raw: Box<str>) -> Box<RawJson> {
if raw.is_empty() {
Self::new_boxed_unchecked("null".into())
} else {
unsafe { Box::from_raw(Box::into_raw(raw) as *mut RawJson) }
}
}
}
impl std::fmt::Debug for RawJson {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(**self).fmt(f)
}
}
impl std::ops::Deref for RawJson {
type Target = MaybeJson;
fn deref(&self) -> &Self::Target {
unsafe { &*(self as *const RawJson as *const MaybeJson) }
}
}
#[repr(transparent)]
pub struct MaybeJson {
pub(crate) raw: str,
}
pub fn drill(input: &str) -> &MaybeJson {
MaybeJson::new(input)
}
struct JsonErrorInner {
error: &'static DecodeError,
context: Option<String>,
parent_context: JsonParentContext,
index: usize,
surrounding: [u8; 24],
}
impl JsonErrorInner {
fn near_by_input(&self) -> &[u8] {
&self.surrounding[0..self.surrounding[23] as usize]
}
}
pub struct JsonError {
inner: Box<JsonErrorInner>,
}
impl JsonError {
pub fn index(&self) -> usize {
self.inner.index
}
pub fn decoding_error(&self) -> &'static DecodeError {
self.inner.error
}
#[cold]
fn trailing() -> JsonError {
JsonError {
inner: Box::new(JsonErrorInner {
error: &DecodeError {
message: "Trailing characters",
},
context: None,
parent_context: JsonParentContext::None,
index: 0,
surrounding: [0; 24],
}),
}
}
pub fn new(error: &'static DecodeError, context: Option<String>) -> JsonError {
JsonError {
inner: Box::new(JsonErrorInner {
error,
context,
parent_context: JsonParentContext::None,
index: 0,
surrounding: [0; 24],
}),
}
}
pub fn extract(error: &'static DecodeError, parser: &mut Parser) -> JsonError {
fn surrounding(at: usize, text: &[u8]) -> [u8; 24] {
let mut s: [u8; 24] = [0; 24];
let end = (at + 12).min(text.len());
let start = end.saturating_sub(23);
let ctx = &text[start..end];
s[23] = ctx.len() as u8;
s[0..ctx.len()].copy_from_slice(ctx);
s
}
JsonError {
inner: Box::new(JsonErrorInner {
error,
context: parser.at.ctx.error.take().map(|x| x.to_string()),
parent_context: parser.parent_context,
index: parser.at.index,
surrounding: surrounding(parser.at.index, parser.at.ctx.input),
}),
}
}
}
impl std::error::Error for JsonError {}
impl std::fmt::Debug for JsonError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<JsonError as std::fmt::Display>::fmt(self, f)
}
}
impl std::fmt::Display for JsonError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.inner.error.message)?;
if let Some(context) = &self.inner.context {
f.write_str(": ")?;
f.write_str(context)?;
}
match &self.inner.parent_context {
JsonParentContext::ObjectKey(key) => {
write!(f, " @ key {:?}", key)?;
}
JsonParentContext::Schema { schema, mask } => {
if std::ptr::eq(self.inner.error, &crate::error::MISSING_REQUIRED_FIELDS) {
write!(f, ": ")?;
let mut first = true;
for (index, field) in schema.fields.iter().enumerate() {
if mask & (1 << index) != 0 {
if !first {
f.write_str(", ")?;
}
first = false;
write!(f, "{:?}", field.name)?;
}
}
return Ok(());
}
}
JsonParentContext::SchemaField { schema, index } => {
if std::ptr::eq(self.inner.error, &crate::error::MISSING_REQUIRED_FIELDS) {
if let Some(field) = schema.fields.get(*index) {
write!(f, ": {:?}", field.name)?;
return Ok(());
}
}
}
_ => (),
}
write!(f, " near `{}`", self.inner.near_by_input().escape_ascii())
}
}
#[derive(Clone, Copy)]
#[repr(align(8))]
pub struct JsonParserConfig {
pub recursion_limit: i32,
pub allow_trailing_commas: bool,
pub allow_comments: bool,
pub allow_unquoted_field_keys: bool,
pub allow_trailing_data: bool,
}
impl Default for JsonParserConfig {
fn default() -> Self {
Self {
recursion_limit: 128,
allow_trailing_commas: false,
allow_comments: false,
allow_unquoted_field_keys: false,
allow_trailing_data: false,
}
}
}
pub fn from_json<'a, T: FromJson<'a>>(json: &'a str) -> Result<T, JsonError> {
from_json_with_config(
json,
const {
JsonParserConfig {
recursion_limit: 128,
allow_trailing_commas: false,
allow_comments: false,
allow_unquoted_field_keys: false,
allow_trailing_data: false,
}
},
)
}
pub fn from_json_bytes<'a, T: FromJson<'a>>(json: &'a [u8]) -> Result<T, JsonError> {
match std::str::from_utf8(json) {
Ok(value) => from_json(value),
Err(err) => Err(JsonError::new(&INVALID_UTF8, Some(err.to_string()))),
}
}
static INVALID_UTF8: DecodeError = DecodeError {
message: "Invalid UTF-8",
};
#[inline]
pub fn from_json_with_config<'a, T: FromJson<'a>>(
json: &'a str,
config: JsonParserConfig,
) -> Result<T, JsonError> {
unsafe fn inner_from_json<'a>(
value: NonNull<()>,
func: unsafe fn(NonNull<()>, &mut Parser<'a>) -> Result<(), &'static DecodeError>,
json: &'a str,
config: JsonParserConfig,
) -> Result<bool, JsonError> {
let mut parser = Parser::new(json, config);
#[cfg(not(feature = "json_comments"))]
if config.allow_comments {
panic!(
"jsony: 'json_comments' feature is not enabled but is required for `allow_comments`."
)
}
match unsafe { func(value, &mut parser) } {
Ok(()) => Ok(config.allow_trailing_data || parser.at.eat_whitespace().is_none()),
Err(err) => Err(JsonError::extract(err, &mut parser)),
}
}
let mut value = std::mem::MaybeUninit::<T>::uninit();
match unsafe {
inner_from_json(
NonNull::new_unchecked(value.as_mut_ptr()).cast(),
T::emplace_from_json,
json,
config,
)
} {
Ok(true) => Ok(unsafe { value.assume_init() }),
Ok(false) => {
unsafe {
value.assume_init_drop();
}
Err(JsonError::trailing())
}
Err(err) => Err(err),
}
}
pub fn to_json<T: ToJson + ?Sized>(value: &T) -> String {
let mut buf = TextWriter::new();
value.encode_json__jsony(&mut buf);
buf.into_string()
}
pub fn to_json_into<'a, T: ToJson + ?Sized, W: IntoTextWriter<'a>>(
value: &T,
output: W,
) -> W::Output {
let mut buffer = W::into_text_writer(output);
value.encode_json__jsony(&mut buffer);
W::finish_writing(buffer)
}
pub fn from_binary<'a, T: FromBinary<'a>>(slice: &'a [u8]) -> Result<T, FromBinaryError> {
let mut decoder = Decoder::new(slice);
let value = Ok(T::decode_binary(&mut decoder));
if let Some(error) = decoder.consume_error() {
Err(error)
} else {
value
}
}
pub fn to_binary<T: ToBinary + ?Sized>(value: &T) -> Vec<u8> {
let mut encoder = BytesWriter::new();
value.encode_binary(&mut encoder);
encoder.into_vec()
}
pub fn to_binary_into<'a, T: ToBinary + ?Sized, W: IntoByteWriter<'a>>(
value: &T,
output: W,
) -> W::Output {
let mut buffer = W::into_byte_writer(output);
value.encode_binary(&mut buffer);
W::finish_writing(buffer)
}
#[macro_export]
macro_rules! require {
(|$field:ident $(: $type: ty)?| $expr: expr, $message: literal) => {
|$field $(: $type)?| {
if $expr {
Ok(())
} else {
Err(format!("{:?} was invalid: {}", $field, $message))
}
}
};
($required_pattern: pat) => {
|value| match *value {
$required_pattern => Ok(()),
_ => Err(format!(
"Got `{:?}` which does not match the required pattern `{}`",
value,
stringify!($required_pattern)
)),
}
};
($required_pattern: pat, $message: literal) => {
|value| match *value {
$required_pattern => Ok(()),
_ => Err(format!("{:?} was invalid: {}", value, $message)),
}
};
}