130 lines
4.2 KiB
Rust
130 lines
4.2 KiB
Rust
use crate::err::Error;
|
|
use crate::sql::operation::Op;
|
|
use crate::sql::value::Value;
|
|
|
|
impl Value {
|
|
pub(crate) fn patch(&mut self, val: Value) -> Result<(), Error> {
|
|
for o in val.to_operations()?.into_iter() {
|
|
match o.op {
|
|
Op::Add => match self.pick(&o.path) {
|
|
Value::Array(_) => self.inc(&o.path, o.value),
|
|
_ => self.put(&o.path, o.value),
|
|
},
|
|
Op::Remove => self.cut(&o.path),
|
|
Op::Replace => self.put(&o.path, o.value),
|
|
Op::Change => {
|
|
if let Value::Strand(p) = o.value {
|
|
if let Value::Strand(v) = self.pick(&o.path) {
|
|
let dmp = dmp::new();
|
|
let pch = dmp.patch_from_text(p.as_string()).map_err(|e| {
|
|
Error::InvalidPatch {
|
|
message: format!("{e:?}"),
|
|
}
|
|
})?;
|
|
let (txt, _) = dmp.patch_apply(&pch, v.as_str()).map_err(|e| {
|
|
Error::InvalidPatch {
|
|
message: format!("{e:?}"),
|
|
}
|
|
})?;
|
|
let txt = txt.into_iter().collect::<String>();
|
|
self.put(&o.path, Value::from(txt));
|
|
}
|
|
}
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
|
|
use super::*;
|
|
use crate::sql::test::Parse;
|
|
|
|
#[tokio::test]
|
|
async fn patch_add_simple() {
|
|
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
|
let ops = Value::parse("[{ op: 'add', path: '/temp', value: true }]");
|
|
let res = Value::parse("{ test: { other: null, something: 123 }, temp: true }");
|
|
val.patch(ops).unwrap();
|
|
assert_eq!(res, val);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn patch_remove_simple() {
|
|
let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }");
|
|
let ops = Value::parse("[{ op: 'remove', path: '/temp' }]");
|
|
let res = Value::parse("{ test: { other: null, something: 123 } }");
|
|
val.patch(ops).unwrap();
|
|
assert_eq!(res, val);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn patch_replace_simple() {
|
|
let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }");
|
|
let ops = Value::parse("[{ op: 'replace', path: '/temp', value: 'text' }]");
|
|
let res = Value::parse("{ test: { other: null, something: 123 }, temp: 'text' }");
|
|
val.patch(ops).unwrap();
|
|
assert_eq!(res, val);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn patch_change_simple() {
|
|
let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: 'test' }");
|
|
let ops = Value::parse(
|
|
"[{ op: 'change', path: '/temp', value: '@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n' }]",
|
|
);
|
|
let res = Value::parse("{ test: { other: null, something: 123 }, temp: 'text' }");
|
|
val.patch(ops).unwrap();
|
|
assert_eq!(res, val);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn patch_add_embedded() {
|
|
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
|
let ops = Value::parse("[{ op: 'add', path: '/temp/test', value: true }]");
|
|
let res = Value::parse("{ test: { other: null, something: 123 }, temp: { test: true } }");
|
|
val.patch(ops).unwrap();
|
|
assert_eq!(res, val);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn patch_remove_embedded() {
|
|
let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }");
|
|
let ops = Value::parse("[{ op: 'remove', path: '/test/other' }]");
|
|
let res = Value::parse("{ test: { something: 123 }, temp: true }");
|
|
val.patch(ops).unwrap();
|
|
assert_eq!(res, val);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn patch_replace_embedded() {
|
|
let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }");
|
|
let ops = Value::parse("[{ op: 'replace', path: '/test/other', value: 'text' }]");
|
|
let res = Value::parse("{ test: { other: 'text', something: 123 }, temp: true }");
|
|
val.patch(ops).unwrap();
|
|
assert_eq!(res, val);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn patch_change_embedded() {
|
|
let mut val = Value::parse("{ test: { other: 'test', something: 123 }, temp: true }");
|
|
let ops = Value::parse(
|
|
"[{ op: 'change', path: '/test/other', value: '@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n' }]",
|
|
);
|
|
let res = Value::parse("{ test: { other: 'text', something: 123 }, temp: true }");
|
|
val.patch(ops).unwrap();
|
|
assert_eq!(res, val);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn patch_change_invalid() {
|
|
// See https://github.com/surrealdb/surrealdb/issues/2001
|
|
let mut val = Value::parse("{ test: { other: 'test', something: 123 }, temp: true }");
|
|
let ops = Value::parse("[{ op: 'change', path: '/test/other', value: 'text' }]");
|
|
assert!(val.patch(ops).is_err());
|
|
}
|
|
}
|