Repository: MiSawa/xq (rust)

Issue:
Update with nested array slicing behavior difference
Using nested slicing on LHS of update operator causes behavior difference against jq.
```
❯ jq -nc '[1,2,3] | .[1:][:1] = [5]'
[1,5,3]

❯ xq -nc '[1,2,3] | .[1:][:1] = [5]'
[1,5]

❯ xq --version
xq 0.1.9-b5d6c8d747459413aadf8298237f1628edbbd9e7
```

Pull request:
Fix update slice
Fixes #48 

Patch:
diff --git a/src/intrinsic/index.rs b/src/intrinsic/index.rs
index 3ad457a..b193890 100644
--- a/src/intrinsic/index.rs
+++ b/src/intrinsic/index.rs
@@ -133,7 +133,11 @@ pub(crate) fn calculate_slice_index(
         Bound::Excluded(i) => i,
         Bound::Unbounded => length,
     };
-    Ok(if start <= end { start..end } else { 0..0 })
+    Ok(if start <= end {
+        start..end
+    } else {
+        start..start
+    })
 }
 
 pub(crate) fn slice(
diff --git a/src/intrinsic/path.rs b/src/intrinsic/path.rs
index 4ca4fde..6125889 100644
--- a/src/intrinsic/path.rs
+++ b/src/intrinsic/path.rs
@@ -153,20 +153,23 @@ fn set_path_rec(context: Value, path: &[Value], replacement: Value) -> Result<Va
                 arr.into()
             }
             (Value::Array(arr), Value::Object(map)) => {
+                // If it was empty, insert the thing into the starting point.
+                // map_to_slice_range returns start..start if the range was empty so this code
+                // should be fine.
                 let range = map_to_slice_range(arr.len(), map)?;
-                if range.is_empty() {
-                    // FIXME: JQ seems to handle this differently...
-                    return Ok(Value::Array(arr));
-                }
-                let replacement = set_path_rec(Value::Null, path, replacement)?;
+                let mut before = make_owned(arr);
+                let after = before.split_off(range.end);
+                let middle = if range.start == 0 {
+                    std::mem::take(&mut before)
+                } else {
+                    before.split_off(range.start)
+                };
+                let replacement = set_path_rec(middle.into(), path, replacement)?;
                 if let Value::Array(replacement) = replacement {
-                    // TODO: test this
-                    let before = &arr[..range.start];
-                    let after = &arr[range.end..];
                     before
                         .iter()
                         .chain(replacement.as_ref())
-                        .chain(after)
+                        .chain(after.iter())
                         .cloned()
                         .collect::<Array>()
                         .into()
diff --git a/src/lang/lexer.rs b/src/lang/lexer.rs
index 015e30d..296241c 100644
--- a/src/lang/lexer.rs
+++ b/src/lang/lexer.rs
@@ -203,7 +203,7 @@ impl<'input> IntoIterator for Lexer<'input> {
                         }
                     }
                 }
-                return Err(LexicalError::OrphanToken("catch".to_string()));
+                Err(LexicalError::OrphanToken("catch".to_string()))
             }
             fn close_autoclose(&mut self) -> Option<Token<'input>> {
                 while let Some(item) = self.stack.last() {
diff --git a/src/value.rs b/src/value.rs
index ea1f50d..45b9a78 100644
--- a/src/value.rs
+++ b/src/value.rs
@@ -83,6 +83,10 @@ impl Array {
     pub fn remove(&mut self, index: usize) -> Value {
         self.0.remove(index)
     }
+
+    pub fn split_off(&mut self, index: usize) -> Self {
+        Self(self.0.split_off(index))
+    }
 }
 
 impl Object {


Test patch:
diff --git a/tests/hand_written/assignments.rs b/tests/hand_written/assignments.rs
index b2203ed..34cd18a 100644
--- a/tests/hand_written/assignments.rs
+++ b/tests/hand_written/assignments.rs
@@ -64,3 +64,55 @@ test!(
     [3, 2, 3]
     "#
 );
+
+test!(
+    slice_update1,
+    r#"
+    [1,2,3] | .[1:][:1] = [5]
+    "#,
+    r#"
+    null
+    "#,
+    r#"
+    [1,5,3]
+    "#
+);
+
+test!(
+    slice_update2,
+    r#"
+    [1,2,3] | .[0:3][1:][:1] = [5]
+    "#,
+    r#"
+    null
+    "#,
+    r#"
+    [1,5,3]
+    "#
+);
+
+test!(
+    slice_update3,
+    r#"
+    [1,2,3] | .[2:1][:1] = [5]
+    "#,
+    r#"
+    null
+    "#,
+    r#"
+    [1,2,5,3]
+    "#
+);
+
+test!(
+    slice_update4,
+    r#"
+    [1,2,3] | .[2:1][2] = 5
+    "#,
+    r#"
+    null
+    "#,
+    r#"
+    [1,2,null,null,5,3]
+    "#
+);