use crate::store::CodecFormat;
use crate::store::screening::Noticed;
use crate::store::{Occupied, StorageError, StorageResult};
use amethystate_core::path::StorePath;
use amethystate_core::path::{SmolStr, Stored};
use error_stack::Report;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt::Debug;
pub trait TextDocument: Send + Sync + Sized + Clone + 'static {
type Node: Clone + Debug + Navigable;
fn format() -> CodecFormat;
fn get(&self, at: &StorePath) -> Option<&Self::Node>;
fn set(&mut self, at: &StorePath, node: Self::Node) -> StorageResult<()>;
fn set_over(&mut self, at: &StorePath, node: Self::Node) -> StorageResult<()>;
fn delete(&mut self, at: &StorePath) -> StorageResult<Option<Self::Node>>;
fn delete_subtree(&mut self, at: &StorePath) -> StorageResult<()>;
fn scan(&self, prefix: &StorePath) -> StorageResult<Vec<(StorePath, Self::Node)>>;
fn scan_keys(&self, prefix: &StorePath) -> StorageResult<Vec<StorePath>>;
fn parse(src: &str) -> StorageResult<Self>;
fn serialize(&self) -> StorageResult<String>;
fn empty() -> Self;
fn deserialize_node<T: DeserializeOwned>(node: &Self::Node) -> StorageResult<T>;
fn serialize_node<T: Serialize + ?Sized>(
value: &T,
seen: &Noticed,
) -> StorageResult<Self::Node>;
fn node_to_bytes(node: &Self::Node) -> StorageResult<Vec<u8>>;
fn bytes_to_node(bytes: &[u8]) -> StorageResult<Self::Node>;
fn with_bytes_de(
bytes: &[u8],
f: &mut dyn FnMut(&mut dyn erased_serde::Deserializer) -> StorageResult<()>,
) -> StorageResult<()>;
}
pub trait Navigable: Sized + Clone {
fn make_empty_map() -> Self;
fn get_child(&self, key: Stored<'_>) -> Option<&Self>;
fn get_child_mut(&mut self, key: Stored<'_>) -> Option<&mut Self>;
fn is_map(&self) -> bool;
fn has_children(&self) -> bool;
fn insert_child(&mut self, key: Stored<'_>, val: Self);
fn remove_child(&mut self, key: Stored<'_>) -> Option<Self>;
fn scan_children(&self) -> Vec<(SmolStr, Self)>;
fn child_names(&self) -> Vec<SmolStr>;
fn each_child(&self) -> impl Iterator<Item = (&str, &Self)>;
#[cfg(feature = "bench-internals")]
fn children(&self) -> Vec<(&str, &Self)> {
self.each_child().collect()
}
fn known_same(&self, other: &Self) -> bool;
}
#[cfg(any(feature = "json", feature = "ron"))]
macro_rules! walks_one_node {
() => {
fn get(&self, at: &StorePath) -> Option<&Self::Node> {
generic_get(&self.0, at)
}
fn set(&mut self, at: &StorePath, node: Self::Node) -> StorageResult<()> {
if at.is_root() {
if !Navigable::is_map(&node) {
return Err(Report::new(TextStoreError::RootMustBeObject)
.change_context(StorageError::Write)
.attach("the write was addressed at the document root"));
}
self.0 = node;
return Ok(());
}
generic_set(&mut self.0, at, node)
}
fn set_over(&mut self, at: &StorePath, node: Self::Node) -> StorageResult<()> {
if at.is_root() {
return self.set(at, node);
}
generic_set_over(&mut self.0, at, node)
}
fn delete(&mut self, at: &StorePath) -> StorageResult<Option<Self::Node>> {
generic_delete(&mut self.0, at)
}
fn delete_subtree(&mut self, at: &StorePath) -> StorageResult<()> {
generic_delete_subtree(&mut self.0, at)
}
fn scan(&self, prefix: &StorePath) -> StorageResult<Vec<(StorePath, Self::Node)>> {
generic_scan(&self.0, prefix)
}
fn scan_keys(&self, prefix: &StorePath) -> StorageResult<Vec<StorePath>> {
generic_scan_keys(&self.0, prefix)
}
};
}
#[cfg(any(feature = "json", feature = "ron"))]
pub(crate) use walks_one_node;
pub fn generic_get<'a, N: Navigable>(root: &'a N, at: &StorePath) -> Option<&'a N> {
let mut current = root;
for name in at.segments() {
current = current.get_child(Stored::level(&name))?;
}
Some(current)
}
pub fn generic_set<N: Navigable>(root: &mut N, at: &StorePath, node: N) -> StorageResult<()> {
set_in(root, at, node, false)
}
pub fn generic_set_over<N: Navigable>(root: &mut N, at: &StorePath, node: N) -> StorageResult<()> {
set_in(root, at, node, true)
}
fn set_in<N: Navigable>(root: &mut N, at: &StorePath, node: N, over: bool) -> StorageResult<()> {
let Some(last) = at.name() else {
*root = node;
return Ok(());
};
let heads = at.len() - 1;
let mut current = root;
for (depth, name) in at.segments().take(heads).enumerate() {
if !current.is_map() {
if !over {
return Err(refused(
Occupied::Value {
level: level(at, depth),
},
at,
));
}
*current = N::make_empty_map();
}
if current.get_child(Stored::level(&name)).is_none() {
current.insert_child(Stored::level(&name), N::make_empty_map());
}
current = current
.get_child_mut(Stored::level(&name))
.expect("just inserted");
}
if !current.is_map() {
if !over {
return Err(refused(
Occupied::Value {
level: level(at, heads),
},
at,
));
}
*current = N::make_empty_map();
}
if !over
&& !node.is_map()
&& let Some(existing) = current.get_child(Stored::level(&last))
&& existing.is_map()
&& existing.has_children()
{
return Err(refused(
Occupied::Branch {
level: level(at, at.len()),
},
at,
));
}
current.insert_child(Stored::level(&last), node);
Ok(())
}
fn level(at: &StorePath, upto: usize) -> String {
StorePath::from_segments(at.segments().take(upto)).to_string()
}
fn refused(occupied: Occupied, writing: &StorePath) -> Report<StorageError> {
Report::new(occupied)
.change_context(StorageError::Write)
.attach(format!("writing: {writing}"))
.attach("a document holds a value at a level or values under it, never both")
}
pub fn generic_delete<N: Navigable>(root: &mut N, at: &StorePath) -> StorageResult<Option<N>> {
let Some(last) = at.name() else {
return Ok(None);
};
let heads = at.len() - 1;
let mut current = &mut *root;
for name in at.segments().take(heads) {
match current.get_child_mut(Stored::level(&name)) {
Some(next) => current = next,
None => return Ok(None),
}
}
let removed = current.remove_child(Stored::level(&last));
if removed.is_some() {
prune_empty_above(root, at, heads);
}
Ok(removed)
}
fn prune_empty_above<N: Navigable>(root: &mut N, at: &StorePath, heads: usize) {
for depth in (1..=heads).rev() {
let name = at.segment_at(depth - 1).expect("above the delete");
let mut current = &mut *root;
for above in at.segments().take(depth - 1) {
match current.get_child_mut(Stored::level(&above)) {
Some(next) => current = next,
None => return,
}
}
match current.get_child(Stored::level(&name)) {
Some(node) if node.is_map() && !node.has_children() => {
current.remove_child(Stored::level(&name))
}
_ => return,
};
}
}
pub fn generic_delete_subtree<N: Navigable>(root: &mut N, at: &StorePath) -> StorageResult<()> {
let Some(last) = at.name() else {
*root = N::make_empty_map();
return Ok(());
};
let heads = at.len() - 1;
let mut current = root;
for name in at.segments().take(heads) {
match current.get_child_mut(Stored::level(&name)) {
Some(next) => current = next,
None => return Ok(()),
}
}
current.remove_child(Stored::level(&last));
Ok(())
}
pub fn generic_scan<N: Navigable>(
root: &N,
prefix: &StorePath,
) -> StorageResult<Vec<(StorePath, N)>> {
let mut results = Vec::new();
if let Some(node) = generic_get(root, prefix) {
for (k, v) in node.scan_children() {
results.push((prefix.push_shared(k), v));
}
}
Ok(results)
}
pub fn generic_scan_keys<N: Navigable>(
root: &N,
prefix: &StorePath,
) -> StorageResult<Vec<StorePath>> {
let mut results = Vec::new();
if let Some(node) = generic_get(root, prefix) {
for name in node.child_names() {
results.push(prefix.push_shared(name));
}
}
Ok(results)
}