Skip to main content

surrealdb_expr/val/value/
set.rs

1use crate::val::field_path::{FieldPath, FieldPathPart};
2use crate::val::{Object, Value};
3
4impl Value {
5	/// Synchronous method for setting a value at a FieldPath, consuming `val`.
6	///
7	/// Unlike `set()`, this doesn't require async/computation - just navigation.
8	/// When encountering an array, applies the remaining path to all elements
9	/// (cloning `val` for all-but-last and moving into the last). Otherwise
10	/// `val` is moved straight to the leaf assignment. This is used for output
11	/// path navigation in the projection operator, where each computed field
12	/// value is produced fresh and would otherwise be cloned into the output.
13	pub fn set_at_field_path_owned(&mut self, path: &FieldPath, val: Value) {
14		self.set_at_field_path_owned_depth(path, val, 0);
15	}
16
17	fn set_at_field_path_owned_depth(&mut self, path: &FieldPath, val: Value, depth: usize) {
18		if depth >= path.len() {
19			*self = val;
20			return;
21		}
22
23		let part = &path.0[depth];
24		let is_last = depth == path.len() - 1;
25
26		match (self, part) {
27			// Object with Field/Lookup key
28			(Value::Object(obj), FieldPathPart::Field(key) | FieldPathPart::Lookup(key)) => {
29				if is_last {
30					obj.insert(key.clone(), val);
31				} else {
32					let nested =
33						obj.entry(key.clone()).or_insert_with(|| Value::Object(Object::default()));
34					nested.set_at_field_path_owned_depth(path, val, depth + 1);
35				}
36			}
37			// Index on array - must come before the catch-all array pattern
38			(Value::Array(arr), FieldPathPart::Index(i)) if *i < arr.len() => {
39				if is_last {
40					arr[*i] = val;
41				} else {
42					arr[*i].set_at_field_path_owned_depth(path, val, depth + 1);
43				}
44			}
45			// First element
46			(Value::Array(arr), FieldPathPart::First) if !arr.is_empty() => {
47				if is_last {
48					arr[0] = val;
49				} else {
50					arr[0].set_at_field_path_owned_depth(path, val, depth + 1);
51				}
52			}
53			// Last element
54			(Value::Array(arr), FieldPathPart::Last) if !arr.is_empty() => {
55				let last_idx = arr.len() - 1;
56				if is_last {
57					arr[last_idx] = val;
58				} else {
59					arr[last_idx].set_at_field_path_owned_depth(path, val, depth + 1);
60				}
61			}
62			// Index on set - must come before the catch-all set pattern
63			(Value::Set(set), FieldPathPart::Index(i)) if set.nth(*i).is_some() => {
64				if let Some(elem) = set.nth_mut(*i) {
65					if is_last {
66						*elem = val;
67					} else {
68						elem.set_at_field_path_owned_depth(path, val, depth + 1);
69					}
70				}
71			}
72			// First element of set
73			(Value::Set(set), FieldPathPart::First) if !set.is_empty() => {
74				if let Some(elem) = set.first_mut() {
75					if is_last {
76						*elem = val;
77					} else {
78						elem.set_at_field_path_owned_depth(path, val, depth + 1);
79					}
80				}
81			}
82			// Last element of set
83			(Value::Set(set), FieldPathPart::Last) if !set.is_empty() => {
84				if let Some(elem) = set.last_mut() {
85					if is_last {
86						*elem = val;
87					} else {
88						elem.set_at_field_path_owned_depth(path, val, depth + 1);
89					}
90				}
91			}
92			// Array with Field/Lookup key: apply remaining path to all elements.
93			// Clone `val` for all-but-last, move into the last element.
94			(Value::Array(arr), FieldPathPart::Field(_) | FieldPathPart::Lookup(_)) => {
95				if let Some((last, rest)) = arr.split_last_mut() {
96					for elem in rest {
97						elem.set_at_field_path_owned_depth(path, val.clone(), depth);
98					}
99					last.set_at_field_path_owned_depth(path, val, depth);
100				}
101			}
102			// Non-object with Field/Lookup: replace with object containing the path
103			(target, FieldPathPart::Field(key) | FieldPathPart::Lookup(key)) => {
104				let mut obj = Object::default();
105				if is_last {
106					obj.insert(key.clone(), val);
107				} else {
108					let mut nested = Value::Object(Object::default());
109					nested.set_at_field_path_owned_depth(path, val, depth + 1);
110					obj.insert(key.clone(), nested);
111				}
112				*target = Value::Object(obj);
113			}
114			_ => {} // Other cases: no-op (val is dropped)
115		}
116	}
117}