Skip to main content

surrealdb_expr/val/value/
put.rs

1use surrealdb_types::ToSql;
2
3use crate::expr::part::{Next, Part};
4use crate::val::Value;
5
6impl Value {
7	/// Synchronous method for setting a field on a `Value`
8	pub fn put(&mut self, path: &[Part], val: Value) {
9		match path.first() {
10			// Get the current value at path
11			Some(p) => match self {
12				// Current value at path is an object
13				Value::Object(v) => match p {
14					Part::Lookup(lookup) => {
15						let entry = v.entry(lookup.to_sql()).or_insert_with(Value::empty_object);
16						if !entry.is_nullish() {
17							entry.put(path.next(), val);
18						} else {
19							let mut obj = Value::empty_object();
20							obj.put(path.next(), val);
21							v.insert(lookup.to_sql(), obj);
22						}
23					}
24					Part::Field(f) => {
25						let entry = v.entry(f.clone()).or_insert_with(Value::empty_object);
26						entry.put(path.next(), val);
27					}
28					Part::All => {
29						let path = path.next();
30						v.iter_mut().for_each(|(_, v)| v.put(path, val.clone()));
31					}
32					x => {
33						if let Some(idx) = x.as_old_index() {
34							let entry =
35								v.entry(idx.to_string()).or_insert_with(Value::empty_object);
36							entry.put(path.next(), val);
37						}
38					}
39				},
40				// Current value at path is an array
41				Value::Array(v) => match p {
42					Part::All => {
43						let path = path.next();
44						v.iter_mut().for_each(|v| v.put(path, val.clone()));
45					}
46					Part::First => {
47						if let Some(v) = v.first_mut() {
48							v.put(path.next(), val)
49						}
50					}
51					Part::Last => {
52						if let Some(v) = v.last_mut() {
53							v.put(path.next(), val)
54						}
55					}
56					x => {
57						if let Some(idx) = x.as_old_index() {
58							if let Some(v) = v.get_mut(idx) {
59								v.put(path.next(), val)
60							}
61						} else {
62							v.iter_mut().for_each(|v| v.put(path, val.clone()));
63						}
64					}
65				},
66				// Current value at path is a set
67				Value::Set(v) => match p {
68					Part::All => {
69						let path = path.next();
70						let len = v.len();
71						for i in 0..len {
72							if let Some(elem) = v.nth_mut(i) {
73								elem.put(path, val.clone());
74							}
75						}
76					}
77					Part::First => {
78						if let Some(v) = v.first_mut() {
79							v.put(path.next(), val)
80						}
81					}
82					Part::Last => {
83						if let Some(v) = v.last_mut() {
84							v.put(path.next(), val)
85						}
86					}
87					x => {
88						if let Some(idx) = x.as_old_index() {
89							if let Some(v) = v.nth_mut(idx) {
90								v.put(path.next(), val)
91							}
92						} else {
93							let len = v.len();
94							for i in 0..len {
95								if let Some(elem) = v.nth_mut(i) {
96									elem.put(path, val.clone());
97								}
98							}
99						}
100					}
101				},
102				// Current value at path is empty
103				Value::Null => {
104					*self = Value::empty_object();
105					self.put(path, val)
106				}
107				// Current value at path is empty
108				Value::None => {
109					*self = Value::empty_object();
110					self.put(path, val)
111				}
112				// Ignore everything else
113				_ => (),
114			},
115			// No more parts so put the value
116			None => {
117				*self = val;
118			}
119		}
120	}
121}
122
123#[cfg(test)]
124mod tests {
125
126	use super::*;
127	use crate::expr::idiom::Idiom;
128	use crate::syn;
129
130	macro_rules! parse_val {
131		($input:expr) => {
132			crate::val::convert_public_value_to_internal(syn::value($input).unwrap())
133		};
134	}
135
136	#[tokio::test]
137	async fn put_none() {
138		let idi: Idiom = Idiom::default();
139		let mut val: Value = parse_val!("{ test: { other: null, something: 123 } }");
140		let res: Value = parse_val!("999");
141		val.put(&idi, Value::from(999));
142		assert_eq!(res, val);
143	}
144
145	#[tokio::test]
146	async fn put_empty() {
147		let idi: Idiom = syn::idiom("test").unwrap().into();
148		let mut val = Value::None;
149		let res: Value = parse_val!("{ test: 999 }");
150		val.put(&idi, Value::from(999));
151		assert_eq!(res, val);
152	}
153
154	#[tokio::test]
155	async fn put_blank() {
156		let idi: Idiom = syn::idiom("test.something").unwrap().into();
157		let mut val = Value::None;
158		let res: Value = parse_val!("{ test: { something: 999 } }");
159		val.put(&idi, Value::from(999));
160		assert_eq!(res, val);
161	}
162
163	#[tokio::test]
164	async fn put_reput() {
165		let idi: Idiom = syn::idiom("test").unwrap().into();
166		let mut val: Value = parse_val!("{ test: { other: null, something: 123 } }");
167		let res: Value = parse_val!("{ test: 999 }");
168		val.put(&idi, Value::from(999));
169		assert_eq!(res, val);
170	}
171
172	#[tokio::test]
173	async fn put_basic() {
174		let idi: Idiom = syn::idiom("test.something").unwrap().into();
175		let mut val: Value = parse_val!("{ test: { other: null, something: 123 } }");
176		let res: Value = parse_val!("{ test: { other: null, something: 999 } }");
177		val.put(&idi, Value::from(999));
178		assert_eq!(res, val);
179	}
180
181	#[tokio::test]
182	async fn put_allow() {
183		let idi: Idiom = syn::idiom("test.something.allow").unwrap().into();
184		let mut val: Value = parse_val!("{ test: { other: null } }");
185		let res: Value = parse_val!("{ test: { other: null, something: { allow: 999 } } }");
186		val.put(&idi, Value::from(999));
187		assert_eq!(res, val);
188	}
189
190	#[tokio::test]
191	async fn put_wrong() {
192		let idi: Idiom = syn::idiom("test.something.wrong").unwrap().into();
193		let mut val: Value = parse_val!("{ test: { other: null, something: 123 } }");
194		let res: Value = parse_val!("{ test: { other: null, something: 123 } }");
195		val.put(&idi, Value::from(999));
196		assert_eq!(res, val);
197	}
198
199	#[tokio::test]
200	async fn put_other() {
201		let idi: Idiom = syn::idiom("test.other.something").unwrap().into();
202		let mut val: Value = parse_val!("{ test: { other: null, something: 123 } }");
203		let res: Value = parse_val!("{ test: { other: { something: 999 }, something: 123 } }");
204		val.put(&idi, Value::from(999));
205		assert_eq!(res, val);
206	}
207
208	#[tokio::test]
209	async fn put_set_index() {
210		let idi: Idiom = syn::idiom("test[0]").unwrap().into();
211		let mut val: Value = parse_val!("{ test: { 'a', 'b', } }");
212		val.put(&idi, Value::from("hello"));
213		let Value::Object(obj) = &val else {
214			panic!("expected object");
215		};
216		let Value::Set(set) = obj.get("test").unwrap() else {
217			panic!("expected set");
218		};
219		assert_eq!(set.len(), 2);
220		assert_eq!(set.nth(0), Some(&Value::from("hello")));
221		assert_eq!(set.nth(1), Some(&Value::from("b")));
222	}
223
224	#[tokio::test]
225	async fn put_set_index_normalize() {
226		let idi: Idiom = syn::idiom("test[0]").unwrap().into();
227		let mut val: Value = parse_val!("{ test: { 'a', 'b', } }");
228		let res: Value = parse_val!("{ test: { 'b', 'hello', } }");
229		val.put(&idi, Value::from("hello"));
230		// Callers that need set invariants (for example the field pipeline after
231		// `test.*` VALUE) run `Set::normalize` once all element writes are done.
232		if let Value::Object(obj) = &mut val
233			&& let Some(Value::Set(set)) = obj.get_mut("test")
234		{
235			*set = std::mem::take(set).normalize();
236		}
237		assert_eq!(res, val);
238	}
239
240	#[tokio::test]
241	async fn put_array() {
242		let idi: Idiom = syn::idiom("test.something[1]").unwrap().into();
243		let mut val: Value = parse_val!("{ test: { something: [123, 456, 789] } }");
244		let res: Value = parse_val!("{ test: { something: [123, 999, 789] } }");
245		val.put(&idi, Value::from(999));
246		assert_eq!(res, val);
247	}
248
249	#[tokio::test]
250	async fn put_array_field() {
251		let idi: Idiom = syn::idiom("test.something[1].age").unwrap().into();
252		let mut val: Value = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
253		let res: Value = parse_val!("{ test: { something: [{ age: 34 }, { age: 21 }] } }");
254		val.put(&idi, Value::from(21));
255		assert_eq!(res, val);
256	}
257
258	#[tokio::test]
259	async fn put_array_fields() {
260		let idi: Idiom = syn::idiom("test.something[*].age").unwrap().into();
261		let mut val: Value = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
262		let res: Value = parse_val!("{ test: { something: [{ age: 21 }, { age: 21 }] } }");
263		val.put(&idi, Value::from(21));
264		assert_eq!(res, val);
265	}
266
267	#[tokio::test]
268	async fn put_array_fields_flat() {
269		let idi: Idiom = syn::idiom("test.something.age").unwrap().into();
270		let mut val: Value = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
271		let res: Value = parse_val!("{ test: { something: [{ age: 21 }, { age: 21 }] } }");
272		val.put(&idi, Value::from(21));
273		assert_eq!(res, val);
274	}
275}