use crate::Pointer;
pub trait Delete {
type Value;
fn delete(&mut self, ptr: &Pointer) -> Option<Self::Value>;
}
#[cfg(feature = "json")]
mod json {
use super::Delete;
use crate::Pointer;
use core::mem;
use serde_json::Value;
impl Delete for Value {
type Value = Value;
fn delete(&mut self, ptr: &Pointer) -> Option<Self::Value> {
let Some((parent_ptr, last)) = ptr.split_back() else {
return Some(mem::replace(self, Value::Null));
};
parent_ptr
.resolve_mut(self)
.ok()
.and_then(|parent| match parent {
Value::Array(children) => {
let idx = last.to_index().ok()?.for_len_incl(children.len()).ok()?;
children.remove(idx).into()
}
Value::Object(children) => children.remove(last.decoded().as_ref()),
_ => None,
})
}
}
}
#[cfg(feature = "toml")]
mod toml {
use super::Delete;
use crate::Pointer;
use core::mem;
use toml::{Table, Value};
impl Delete for Value {
type Value = Value;
fn delete(&mut self, ptr: &Pointer) -> Option<Self::Value> {
let Some((parent_ptr, last)) = ptr.split_back() else {
return Some(mem::replace(self, Table::default().into()));
};
parent_ptr
.resolve_mut(self)
.ok()
.and_then(|parent| match parent {
Value::Array(children) => {
let idx = last.to_index().ok()?.for_len_incl(children.len()).ok()?;
children.remove(idx).into()
}
Value::Table(children) => children.remove(last.decoded().as_ref()),
_ => None,
})
}
}
}
#[cfg(test)]
mod tests {
use super::Delete;
use crate::Pointer;
use core::fmt;
use serde_json::json;
struct Test<V> {
data: V,
ptr: &'static str,
expected_data: V,
expected_deleted: Option<V>,
}
impl<V> Test<V>
where
V: Delete<Value = V> + Clone + PartialEq + fmt::Display + fmt::Debug,
{
fn all(tests: impl IntoIterator<Item = Test<V>>) {
tests.into_iter().enumerate().for_each(|(i, t)| t.run(i));
}
fn run(self, _i: usize) {
let Test {
mut data,
ptr,
expected_data,
expected_deleted,
} = self;
let ptr = Pointer::from_static(ptr);
let deleted = ptr.delete(&mut data);
assert_eq!(expected_data, data);
assert_eq!(expected_deleted, deleted);
}
}
#[test]
#[cfg(feature = "json")]
fn delete_json() {
Test::all([
Test {
ptr: "/foo",
data: json!({"foo": "bar"}),
expected_data: json!({}),
expected_deleted: Some(json!("bar")),
},
Test {
ptr: "/foo/bar",
data: json!({"foo": {"bar": "baz"}}),
expected_data: json!({"foo": {}}),
expected_deleted: Some(json!("baz")),
},
Test {
ptr: "/foo/bar",
data: json!({"foo": "bar"}),
expected_data: json!({"foo": "bar"}),
expected_deleted: None,
},
Test {
ptr: "/foo/bar",
data: json!({"foo": {"bar": "baz"}}),
expected_data: json!({"foo": {}}),
expected_deleted: Some(json!("baz")),
},
Test {
ptr: "/foo/bar/0",
data: json!({"foo": {"bar": ["baz", "qux"]}}),
expected_data: json!({"foo": {"bar": ["qux"]}}),
expected_deleted: Some(json!("baz")),
},
Test {
ptr: "/foo/0",
data: json!({"foo": "bar"}),
expected_data: json!({"foo": "bar"}),
expected_deleted: None,
},
Test {
ptr: "/foo/bar/0/baz",
data: json!({"foo": { "bar": [{"baz": "qux", "remaining": "field"}]}}),
expected_data: json!({"foo": { "bar": [{"remaining": "field"}]} }),
expected_deleted: Some(json!("qux")),
},
Test {
ptr: "/Example",
data: json!({"Example": 21, "test": "test"}),
expected_data: json!({"test": "test"}),
expected_deleted: Some(json!(21)),
},
Test {
ptr: "",
data: json!({"Example": 21, "test": "test"}),
expected_data: json!(null),
expected_deleted: Some(json!({"Example": 21, "test": "test"})),
},
]);
}
#[test]
#[cfg(feature = "toml")]
fn delete_toml() {
use toml::{toml, Table, Value};
Test::all([
Test {
data: toml! {"foo" = "bar"}.into(),
ptr: "/foo",
expected_data: Value::Table(Table::new()),
expected_deleted: Some("bar".into()),
},
Test {
data: toml! {"foo" = {"bar" = "baz"}}.into(),
ptr: "/foo/bar",
expected_data: toml! {"foo" = {}}.into(),
expected_deleted: Some("baz".into()),
},
Test {
data: toml! {"foo" = "bar"}.into(),
ptr: "/foo/bar",
expected_data: toml! {"foo" = "bar"}.into(),
expected_deleted: None,
},
Test {
data: toml! {"foo" = {"bar" = "baz"}}.into(),
ptr: "/foo/bar",
expected_data: toml! {"foo" = {}}.into(),
expected_deleted: Some("baz".into()),
},
Test {
data: toml! {"foo" = {"bar" = ["baz", "qux"]}}.into(),
ptr: "/foo/bar/0",
expected_data: toml! {"foo" = {"bar" = ["qux"]}}.into(),
expected_deleted: Some("baz".into()),
},
Test {
data: toml! {"foo" = "bar"}.into(),
ptr: "/foo/0",
expected_data: toml! {"foo" = "bar"}.into(),
expected_deleted: None,
},
Test {
data: toml! {"foo" = { "bar" = [{"baz" = "qux", "remaining" = "field"}]}}.into(),
ptr: "/foo/bar/0/baz",
expected_data: toml! {"foo" = { "bar" = [{"remaining" = "field"}]} }.into(),
expected_deleted: Some("qux".into()),
},
Test {
data: toml! {"Example" = 21 "test" = "test"}.into(),
ptr: "/Example",
expected_data: toml! {"test" = "test"}.into(),
expected_deleted: Some(21.into()),
},
]);
}
}