use std::borrow::Cow;
use deser_core::Text;
use deser_core::de::{self, Deserialize, DeserializeDriver};
use deser_core::ext::ExtValue;
use deser_core::hints::Layout;
use deser_core::{Atom, BytesFormat, ContainerShape, Error, ErrorKind, Event, Source};
use crate::document::{Document, Item, Span, TableKind, Value};
use crate::parser::{ROOT, parse};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeserializerConfig {
track_locations: bool,
bytes: BytesFormat,
}
impl Default for DeserializerConfig {
fn default() -> DeserializerConfig {
DeserializerConfig::new()
}
}
impl DeserializerConfig {
pub const fn new() -> DeserializerConfig {
DeserializerConfig {
track_locations: false,
bytes: BytesFormat::BASE64,
}
}
pub const fn bytes(mut self, format: BytesFormat) -> DeserializerConfig {
self.bytes = format;
self
}
pub const fn track_locations(mut self, yes: bool) -> DeserializerConfig {
self.track_locations = yes;
self
}
pub fn from_str<'de, T: Deserialize<'de>>(&self, s: &'de str) -> Result<T, Error> {
Deserializer::from_str_with_config(s, self).deserialize()
}
pub fn from_slice<'de, T: Deserialize<'de>>(&self, bytes: &'de [u8]) -> Result<T, Error> {
Deserializer::from_slice_with_config(bytes, self).deserialize()
}
}
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: config.clone(),
}
}
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: config.clone(),
},
}
}
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 let Some(err) = self.error.take() {
return Err(err);
}
let doc = parse(self.input)?;
if self.config.track_locations {
Source::set(driver.state_mut(), self.input);
}
if self.config.bytes != BytesFormat::BASE64 {
self.config.bytes.set(driver.state_mut());
}
emit(&doc, driver).map_err(|err| err.resolve_position(self.input.as_bytes()))
}
}
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::new().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::new().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::new().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::new(ErrorKind::Unexpected, "invalid UTF-8").with_offset(err.valid_up_to())
})
}
pub fn from_str<'de, T: Deserialize<'de>>(s: &'de str) -> Result<T, Error> {
Deserializer::from_str(s).deserialize()
}
pub fn from_slice<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result<T, Error> {
Deserializer::from_slice(bytes).deserialize()
}