#[cfg(not(test))]
use alloc::{borrow::Cow, string::String, vec::Vec};
#[cfg(test)]
use std::borrow::Cow;
pub trait DocumentCursor: Sized + Copy + Clone {
type Value: DocumentValue<Cursor = Self>;
fn value(&self) -> Self::Value;
fn first_child(&self) -> Option<Self>;
fn next_sibling(&self) -> Option<Self>;
fn parent(&self) -> Option<Self>;
fn is_container(&self) -> bool;
fn text_position(&self) -> Option<usize>;
fn line(&self) -> usize {
0
}
fn column(&self) -> usize {
0
}
fn document_index(&self) -> Option<usize> {
None
}
fn cursor_at_offset(&self, _offset: usize) -> Option<Self> {
None
}
fn cursor_at_position(&self, _line: usize, _col: usize) -> Option<Self> {
None
}
fn stream_json<W: core::fmt::Write>(&self, _out: &mut W) -> core::fmt::Result {
Err(core::fmt::Error)
}
fn stream_yaml<W: core::fmt::Write>(
&self,
_out: &mut W,
_indent_spaces: usize,
) -> core::fmt::Result {
Err(core::fmt::Error)
}
fn is_falsy(&self) -> bool {
false
}
}
pub trait DocumentValue: Sized + Clone {
type Cursor: DocumentCursor<Value = Self>;
type Fields: DocumentFields<Value = Self, Cursor = Self::Cursor>;
type Elements: DocumentElements<Value = Self, Cursor = Self::Cursor>;
fn is_null(&self) -> bool;
fn as_bool(&self) -> Option<bool>;
fn as_i64(&self) -> Option<i64>;
fn as_f64(&self) -> Option<f64>;
fn as_str(&self) -> Option<Cow<'_, str>>;
fn as_object(&self) -> Option<Self::Fields>;
fn as_array(&self) -> Option<Self::Elements>;
fn type_name(&self) -> &'static str;
fn is_error(&self) -> bool;
fn error_message(&self) -> Option<&'static str>;
#[inline]
fn is_bool(&self) -> bool {
self.as_bool().is_some()
}
#[inline]
fn is_number(&self) -> bool {
self.as_i64().is_some() || self.as_f64().is_some()
}
#[inline]
fn is_string(&self) -> bool {
self.as_str().is_some()
}
#[inline]
fn is_array(&self) -> bool {
self.as_array().is_some()
}
#[inline]
fn is_object(&self) -> bool {
self.as_object().is_some()
}
#[inline]
fn is_iterable(&self) -> bool {
self.is_array() || self.is_object()
}
}
#[allow(clippy::type_complexity)]
pub trait DocumentFields: Sized + Copy + Clone {
type Value: DocumentValue;
type Cursor: DocumentCursor;
#[allow(clippy::type_complexity)]
fn uncons(&self) -> Option<(DocumentField<Self::Value, Self::Cursor>, Self)>;
fn find(&self, name: &str) -> Option<Self::Value>;
fn is_empty(&self) -> bool;
fn len(&self) -> usize {
let mut count = 0;
let mut fields = *self;
while let Some((_, rest)) = fields.uncons() {
count += 1;
fields = rest;
}
count
}
fn keys(&self) -> Vec<String> {
let mut keys = Vec::new();
let mut fields = *self;
while let Some((field, rest)) = fields.uncons() {
if let Some(key) = field.key_str() {
keys.push(key.into_owned());
}
fields = rest;
}
keys
}
}
pub struct DocumentField<V, C> {
pub key: V,
pub value: V,
pub value_cursor: C,
}
impl<V: DocumentValue, C: DocumentCursor> DocumentField<V, C> {
pub fn key_str(&self) -> Option<Cow<'_, str>> {
self.key.as_str()
}
}
pub trait DocumentElements: Sized + Copy + Clone {
type Value: DocumentValue;
type Cursor: DocumentCursor;
fn uncons(&self) -> Option<(Self::Value, Self)>;
fn uncons_cursor(&self) -> Option<(Self::Cursor, Self)>;
fn get(&self, index: usize) -> Option<Self::Value>;
fn is_empty(&self) -> bool;
fn len(&self) -> usize {
let mut count = 0;
let mut elems = *self;
while let Some((_, rest)) = elems.uncons() {
count += 1;
elems = rest;
}
count
}
fn collect_values(&self) -> Vec<Self::Value> {
let mut values = Vec::new();
let mut elems = *self;
while let Some((value, rest)) = elems.uncons() {
values.push(value);
elems = rest;
}
values
}
}