surrealpatch/lib/src/sql/value/patch.rs
2023-06-20 12:48:20 +01:00

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());
}
}