use std::borrow::Cow;
use deser_core::Text;
use deser_core::de::{self, Deserialize, DeserializeDriver, deserialize_value};
use deser_core::ext::ExtValue;
use deser_core::hints::Layout;
use deser_core::{Atom, ContainerShape, Error, ErrorKind, Event, Source, TrackLocations};
use crate::document::{Document, Item, Span, TableKind, Value};
use crate::parser::{ROOT, parse};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeserializerConfig {
context: deser_core::Context,
}
impl Default for DeserializerConfig {
fn default() -> DeserializerConfig {
DeserializerConfig::new()
}
}
impl DeserializerConfig {
pub const fn new() -> DeserializerConfig {
DeserializerConfig {
context: deser_core::Context::new(),
}
}
pub const fn builder() -> DeserializerConfigBuilder {
DeserializerConfigBuilder::new()
}
pub const fn into_builder(self) -> DeserializerConfigBuilder {
DeserializerConfigBuilder { value: self }
}
pub fn set_context(&mut self, context: deser_core::Context) {
self.context = context;
}
pub(crate) fn without_context(&self) -> DeserializerConfig {
let mut config = self.clone();
config.context = deser_core::Context::default();
config
}
pub fn context(&self) -> &deser_core::Context {
&self.context
}
pub fn from_str<'de, T: Deserialize<'de>>(&self, s: &'de str) -> Result<T, Error> {
deserialize_value(|driver| self.drive_str(s, driver))
}
fn drive_str<'de>(
&self,
s: &'de str,
driver: &mut DeserializeDriver<'_, 'de>,
) -> Result<(), Error> {
de::Deserializer::drive(
&mut Deserializer::from_str_with_config(s, self.clone()),
driver,
)
}
pub fn from_slice<'de, T: Deserialize<'de>>(&self, bytes: &'de [u8]) -> Result<T, Error> {
deserialize_value(|driver| self.drive_slice(bytes, driver))
}
fn drive_slice<'de>(
&self,
bytes: &'de [u8],
driver: &mut DeserializeDriver<'_, 'de>,
) -> Result<(), Error> {
de::Deserializer::drive(
&mut Deserializer::from_slice_with_config(bytes, self.clone()),
driver,
)
}
}
#[derive(Debug, Clone)]
#[must_use]
pub struct DeserializerConfigBuilder {
value: DeserializerConfig,
}
impl DeserializerConfigBuilder {
pub const fn new() -> DeserializerConfigBuilder {
DeserializerConfigBuilder {
value: DeserializerConfig::new(),
}
}
pub fn context(mut self, context: deser_core::Context) -> DeserializerConfigBuilder {
self.value.set_context(context);
self
}
pub const fn build(self) -> DeserializerConfig {
let value = unsafe { core::ptr::read(&self.value) };
core::mem::forget(self);
value
}
}
impl Default for DeserializerConfigBuilder {
fn default() -> DeserializerConfigBuilder {
DeserializerConfigBuilder::new()
}
}
pub struct Deserializer<'a> {
input: &'a str,
error: Option<Error>,
config: DeserializerConfig,
}
impl<'a> Deserializer<'a> {
#[allow(clippy::should_implement_trait)]
pub fn from_str(input: &'a str) -> Deserializer<'a> {
Deserializer::from_str_with_config(input, DeserializerConfig::new())
}
pub fn from_str_with_config(input: &'a str, config: DeserializerConfig) -> Deserializer<'a> {
Deserializer {
input,
error: None,
config,
}
}
pub fn from_slice(input: &'a [u8]) -> Deserializer<'a> {
Deserializer::from_slice_with_config(input, DeserializerConfig::new())
}
pub fn from_slice_with_config(input: &'a [u8], config: DeserializerConfig) -> Deserializer<'a> {
match str_from_utf8(input) {
Ok(input) => Deserializer::from_str_with_config(input, config),
Err(err) => Deserializer {
input: "",
error: Some(err),
config,
},
}
}
pub fn config(&self) -> &DeserializerConfig {
&self.config
}
pub fn deserialize<T: Deserialize<'a>>(&mut self) -> Result<T, Error> {
de::Deserializer::deserialize(self)
}
pub fn deserialize_with<T, F>(&mut self, setup: F) -> Result<T, Error>
where
T: Deserialize<'a>,
F: FnOnce(&mut DeserializeDriver<'_, 'a>),
{
de::Deserializer::deserialize_with(self, setup)
}
pub fn drive(&mut self, driver: &mut DeserializeDriver<'_, 'a>) -> Result<(), Error> {
if !self.config.context.is_empty() {
driver.set_default_context(self.config.context.clone());
}
if let Some(err) = self.error.take() {
return Err(err);
}
let doc = parse(self.input)?;
if TrackLocations::of(driver.state()) {
Source(self.input.into()).set(driver.state_mut());
}
emit(&doc, driver).map_err(|mut err| {
err.resolve_position(self.input.as_bytes());
err
})
}
}
impl<'a> de::Deserializer<'a> for Deserializer<'a> {
fn drive(&mut self, driver: &mut DeserializeDriver<'_, 'a>) -> Result<(), Error> {
Deserializer::drive(self, driver)
}
}
#[inline(always)]
fn emit_at<'e, E: Into<Event<'e>>>(
driver: &mut DeserializeDriver<'_, '_>,
event: E,
span: Span,
) -> Result<(), Error> {
driver.state_mut().set_input_range(span.start, span.end);
driver.emit(event)
}
#[allow(clippy::ptr_arg)]
#[inline(always)]
fn emit_str<'a>(
driver: &mut DeserializeDriver<'_, 'a>,
value: &Cow<'a, str>,
span: Span,
) -> Result<(), Error> {
driver.state_mut().set_input_range(span.start, span.end);
match *value {
Cow::Borrowed(value) => driver.emit_borrowed(value),
Cow::Owned(ref value) => driver.emit(value.as_str()),
}
}
#[allow(clippy::ptr_arg)]
fn emit_key<'a>(
driver: &mut DeserializeDriver<'_, 'a>,
key: &Cow<'a, str>,
span: Span,
) -> Result<(), Error> {
driver.state_mut().set_input_range(span.start, span.end);
match *key {
Cow::Borrowed(key) => driver.emit_borrowed(Atom::Lexical(Text::borrowed(key))),
Cow::Owned(ref key) => driver.emit(Atom::Lexical(Text::borrowed(key.as_str()))),
}
}
enum Frame {
Table(usize, usize),
Array(usize, usize),
}
fn emit<'a>(doc: &Document<'a>, driver: &mut DeserializeDriver<'_, 'a>) -> Result<(), Error> {
let mut stack = vec![Frame::Table(ROOT, 0)];
let shape = ContainerShape::with_len(doc.tables[ROOT].entries.len());
emit_at(driver, Event::MapStart(shape), doc.tables[ROOT].span)?;
while let Some(frame) = stack.last_mut() {
let item: &Item = match *frame {
Frame::Table(id, ref mut index) => {
let table = &doc.tables[id];
match table.entries.get(*index) {
Some(entry) => {
*index += 1;
emit_key(driver, &entry.key, entry.key_span)?;
&entry.item
}
None => {
stack.pop();
emit_at(driver, Event::MapEnd, table.span)?;
continue;
}
}
}
Frame::Array(id, ref mut index) => {
let array = &doc.arrays[id];
match array.items.get(*index) {
Some(item) => {
*index += 1;
item
}
None => {
stack.pop();
emit_at(driver, Event::SeqEnd, array.span)?;
continue;
}
}
}
};
match item.value {
Value::Table(id) => {
let table = &doc.tables[id];
if table.kind == TableKind::Inline {
Layout::Compact.set(driver.state_mut());
}
let shape = ContainerShape::with_len(table.entries.len());
emit_at(driver, Event::MapStart(shape), table.span)?;
stack.push(Frame::Table(id, 0));
}
Value::Array(id) => {
let array = &doc.arrays[id];
if !array.of_tables
&& array
.items
.first()
.is_some_and(|x| matches!(x.value, Value::Table(_)))
{
Layout::Compact.set(driver.state_mut());
}
let shape = ContainerShape::with_len(array.items.len());
emit_at(driver, Event::SeqStart(shape), array.span)?;
stack.push(Frame::Array(id, 0));
}
Value::Str(ref value) => emit_str(driver, value, item.span)?,
ref scalar => {
let atom = match *scalar {
Value::Int(value) if value >= 0 => Atom::U64(value as u64),
Value::Int(value) => Atom::I64(value),
Value::UInt(value) => Atom::U64(value),
Value::Float(value) => Atom::F64(value),
Value::Bool(value) => Atom::Bool(value),
Value::Datetime(ref value) => Atom::Ext(ExtValue::borrowed(value)),
Value::Str(_) | Value::Table(_) | Value::Array(_) => unreachable!(),
Value::Float32(_) | Value::FloatText(_) => {
unreachable!("only used when serializing")
}
};
emit_at(driver, atom, item.span)?;
}
}
}
Ok(())
}
fn str_from_utf8(bytes: &[u8]) -> Result<&str, Error> {
#[cfg(feature = "speedups")]
{
if simdutf8::basic::from_utf8(bytes).is_ok() {
return Ok(unsafe { std::str::from_utf8_unchecked(bytes) });
}
}
std::str::from_utf8(bytes)
.map_err(|err| Error::with_offset(ErrorKind::Syntax, "invalid UTF-8", err.valid_up_to()))
}
pub fn from_str<'de, T: Deserialize<'de>>(s: &'de str) -> Result<T, Error> {
DeserializerConfig::new().from_str(s)
}
pub fn from_slice<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result<T, Error> {
DeserializerConfig::new().from_slice(bytes)
}