#![doc(html_logo_url = "https://raw.githubusercontent.com/mitsuhiko/deser/main/artwork/logo.svg")]
#![no_std]
extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use deser_core::{Atom, Error, ErrorAttachment, ErrorContext, State};
mod de;
mod ser;
#[derive(Debug, PartialEq, Eq)]
pub enum PathSegment {
Unknown,
Index(usize),
Key(String),
}
impl Clone for PathSegment {
fn clone(&self) -> PathSegment {
match self {
PathSegment::Unknown => PathSegment::Unknown,
PathSegment::Index(index) => PathSegment::Index(*index),
PathSegment::Key(key) => PathSegment::Key(key.clone()),
}
}
fn clone_from(&mut self, source: &PathSegment) {
match (self, source) {
(PathSegment::Key(buf), PathSegment::Key(key)) => buf.clone_from(key),
(this, source) => *this = source.clone(),
}
}
}
#[derive(Default)]
pub struct Path {
segments: Vec<PathSegment>,
spare_keys: Vec<String>,
}
const MAX_SPARE_KEYS: usize = 32;
impl Path {
pub fn segments(&self) -> &[PathSegment] {
&self.segments
}
fn push(&mut self, segment: PathSegment) {
self.segments.push(segment);
}
fn pop(&mut self) {
if let Some(PathSegment::Key(buf)) = self.segments.pop() {
self.recycle(buf);
}
}
fn set_last(&mut self, segment: PathSegment) {
if let Some(last) = self.segments.last_mut() {
*last = segment;
}
}
fn set_last_key(&mut self, atom: &Atom) {
if let (Some(PathSegment::Key(buf)), Atom::Str(key) | Atom::Lexical(key)) =
(self.segments.last_mut(), atom)
{
buf.clear();
buf.push_str(key);
return;
}
let segment = self.key_segment(atom);
if let Some(last) = self.segments.last_mut()
&& let PathSegment::Key(buf) = core::mem::replace(last, segment)
{
self.recycle(buf);
}
}
fn key_segment(&mut self, atom: &Atom) -> PathSegment {
match *atom {
Atom::Str(ref key) | Atom::Lexical(ref key) => {
let mut buf = self.spare_keys.pop().unwrap_or_default();
buf.clear();
buf.push_str(key);
PathSegment::Key(buf)
}
Atom::U64(value) => PathSegment::Index(value as usize),
Atom::I64(value) if value >= 0 => PathSegment::Index(value as usize),
Atom::Ext(ref ext) => {
match ext.fallback() {
Atom::Ext(_) => PathSegment::Unknown,
fallback => self.key_segment(&fallback),
}
}
Atom::Implicit(ref value) => match value.value().to_atom() {
index @ (Atom::U64(_) | Atom::I64(0..)) => self.key_segment(&index),
_ => self.key_segment(&Atom::Str(value.text().as_borrowed())),
},
_ => PathSegment::Unknown,
}
}
fn recycle(&mut self, buf: String) {
if self.spare_keys.len() < MAX_SPARE_KEYS {
self.spare_keys.push(buf);
}
}
}
impl Clone for Path {
fn clone(&self) -> Path {
Path {
segments: self.segments.clone(),
spare_keys: Vec::new(),
}
}
fn clone_from(&mut self, source: &Path) {
self.segments.clone_from(&source.segments);
}
}
impl fmt::Debug for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Path")
.field("segments", &self.segments)
.finish()
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (idx, segment) in self.segments.iter().enumerate() {
match segment {
PathSegment::Key(key) => {
if idx > 0 {
f.write_str(".")?;
}
f.write_str(key)?;
}
PathSegment::Index(index) => write!(f, "[{}]", index)?,
PathSegment::Unknown => {
if idx > 0 {
f.write_str(".")?;
}
f.write_str("?")?;
}
}
}
Ok(())
}
}
impl ErrorAttachment for Path {
fn fmt_context(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, " (path: {})", self)
}
}
#[derive(Debug, Default)]
pub struct PathLayer {
frames: Vec<Frame>,
registered: bool,
}
#[derive(Debug)]
enum Frame {
Map(bool),
Seq(usize),
}
impl PathLayer {
pub fn new() -> PathLayer {
PathLayer::default()
}
#[cold]
fn register(&mut self, state: &mut State, replayable: bool) {
self.registered = true;
state.get_mut::<Path>();
if replayable {
state.set_replayable::<Path>();
}
state.add_error_context::<PathContext>();
}
}
struct PathContext;
impl ErrorContext for PathContext {
fn add_context(err: Error, state: &State) -> Error {
if err.attachment::<Path>().is_some() {
return err;
}
match state.get::<Path>() {
Some(path) if !path.segments.is_empty() => err.with_attachment(path.clone()),
_ => err,
}
}
}