use std::{
cell::{Ref, RefCell},
fmt::{Debug, Display, Write},
};
use smallvec::SmallVec;
use crate::{StaticValue, Value};
pub struct Path {
segments: SmallVec<[PathSegment; 8]>,
str_cache: RefCell<PathStrCache>,
}
struct PathStrCache {
written_lengths: SmallVec<[u16; 8]>,
cache: String,
}
impl Path {
pub(crate) fn new() -> Self {
Self {
segments: Default::default(),
str_cache: RefCell::new(PathStrCache {
written_lengths: Default::default(),
cache: String::new(), }),
}
}
pub(crate) fn push_segment(&mut self, segment: PathSegment) {
self.segments.push(segment);
}
pub(crate) fn pop_segment(&mut self) -> PathSegment {
let res = self.segments.pop().expect("unbalanced pop_segment");
let mut str_cache = self.str_cache.borrow_mut();
while str_cache.written_lengths.len() > self.segments.len() {
unsafe {
let new_len =
str_cache.cache.len() - *str_cache.written_lengths.last().unwrap() as usize;
str_cache.cache.as_mut_vec().truncate(new_len);
}
str_cache.written_lengths.pop();
}
res
}
pub fn segments(&self) -> &[PathSegment] {
&self.segments
}
pub fn is_root(&self) -> bool {
self.segments.is_empty()
}
pub fn borrow_str(&self) -> Ref<'_, String> {
{
let mut str_cache = self.str_cache.borrow_mut();
while str_cache.written_lengths.len() < self.segments.len() {
if str_cache.cache.capacity() == 0 {
str_cache.cache.reserve(256); }
let len_before = str_cache.cache.len();
match &self.segments[str_cache.written_lengths.len()] {
item @ PathSegment::StructField(_) => {
if str_cache.cache.is_empty() {
write!(&mut str_cache.cache, "{item}").expect("path concat failed");
} else {
write!(&mut str_cache.cache, ".{item}").expect("path concat failed");
}
}
item => write!(&mut str_cache.cache, "{item}").expect("path concat failed"),
}
let written_length = (str_cache.cache.len() - len_before) as u16;
str_cache.written_lengths.push(written_length);
}
}
Ref::map(self.str_cache.borrow(), |c| &c.cache)
}
}
impl PartialEq<str> for Path {
fn eq(&self, other: &str) -> bool {
*self.borrow_str() == other
}
}
impl PartialEq<Path> for str {
fn eq(&self, other: &Path) -> bool {
*other.borrow_str() == self
}
}
impl Debug for Path {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.borrow_str().as_str())
}
}
impl Display for Path {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.borrow_str().as_str())
}
}
#[derive(Debug, Clone)]
pub struct PathMapKey {
pub index: usize,
pub value: StaticValue,
}
impl PathMapKey {
pub(crate) fn new(index: usize, value: StaticValue) -> Self {
Self { index, value }
}
}
impl Display for PathMapKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.value {
Value::Bool(_)
| Value::I8(_)
| Value::I16(_)
| Value::I32(_)
| Value::I64(_)
| Value::U8(_)
| Value::U16(_)
| Value::U32(_)
| Value::U64(_)
| Value::F32(_)
| Value::F64(_)
| Value::Char(_)
| Value::Str(_)
| Value::Unit
| Value::Some
| Value::None
| Value::UnitVariant { .. } => Display::fmt(&self.value, f),
_ => Display::fmt(&self.index, f),
}
}
}
#[derive(Debug, Clone)]
pub enum PathSegment {
MapEntry(PathMapKey),
StructField(&'static str),
SeqElement(usize),
}
impl Display for PathSegment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PathSegment::MapEntry(key) => f.write_fmt(format_args!("[{key}]")),
PathSegment::StructField(field_name) => f.write_str(field_name),
PathSegment::SeqElement(index) => f.write_fmt(format_args!("[{index}]")),
}
}
}
impl From<PathMapKey> for PathSegment {
fn from(map_key: PathMapKey) -> Self {
PathSegment::MapEntry(map_key)
}
}