diff options
Diffstat (limited to 'src/document')
| -rw-r--r-- | src/document/store.rs | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/document/store.rs b/src/document/store.rs index 89058fb..13a3fc3 100644 --- a/src/document/store.rs +++ b/src/document/store.rs @@ -15,6 +15,12 @@ pub(crate) enum StoreError { Conflict, #[error("value does not exist")] NotFound, + /// An insert where there is no list. + #[error("not a list")] + NotAList, + /// An insert past a list's end. + #[error("index {index} is past the list's end ({length} items)")] + PastTheEnd { index: usize, length: usize }, #[error(transparent)] Doc(#[from] automerge::AutomergeError), #[error(transparent)] @@ -357,6 +363,29 @@ pub(crate) fn write_path( Ok(()) } +/// Inserts `value` into the list at `path`: at `index` (the items from there on move +/// up one), or at the end without one. Returns where it went. An index past the end is +/// an error (`PastTheEnd`), as is a path without a list (`NotAList`, `NotFound`). +pub(crate) fn insert_path( + doc: &mut AutoCommit, + path: &[String], + index: Option<usize>, + value: &JsonValue, +) -> Result<usize, StoreError> { + let list = match resolve_at(doc, path)? { + None => return Err(StoreError::NotFound), + Some(Resolved::Other(Value::Object(ObjType::List), id)) => id, + Some(_) => return Err(StoreError::NotAList), + }; + let length = doc.length(&list); + let index = index.unwrap_or(length); + if index > length { + return Err(StoreError::PastTheEnd { index, length }); + } + write_value(doc, &list, Slot::Index(index), value)?; + Ok(index) +} + /// The merged maps at `path`, creating missing maps on the way. fn ensure_maps(doc: &mut AutoCommit, path: &[String]) -> Result<Vec<ObjId>, StoreError> { let mut maps = vec![automerge::ROOT]; @@ -806,3 +835,51 @@ fn nested<T>( } out } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn path(at: &str) -> Vec<String> { + at.split('/').map(str::to_owned).collect() + } + + #[test] + fn inserts_into_lists() { + let mut doc = AutoCommit::new(); + write_path(&mut doc, &path("list"), &json!(["b", "d"])).unwrap(); + assert_eq!( + insert_path(&mut doc, &path("list"), Some(0), &json!("a")).unwrap(), + 0 + ); + assert_eq!( + insert_path(&mut doc, &path("list"), Some(2), &json!("c")).unwrap(), + 2 + ); + assert_eq!( + insert_path(&mut doc, &path("list"), None, &json!({"e": 5})).unwrap(), + 4 + ); + assert_eq!( + read_path(&doc, &path("list")).unwrap(), + Some(json!(["a", "b", "c", "d", {"e": 5}])) + ); + assert!(matches!( + insert_path(&mut doc, &path("list"), Some(6), &json!(1)), + Err(StoreError::PastTheEnd { + index: 6, + length: 5 + }) + )); + write_path(&mut doc, &path("map"), &json!({"k": 1})).unwrap(); + assert!(matches!( + insert_path(&mut doc, &path("map"), None, &json!(1)), + Err(StoreError::NotAList) + )); + assert!(matches!( + insert_path(&mut doc, &path("nothing"), None, &json!(1)), + Err(StoreError::NotFound) + )); + } +} |
