1use crate::ctx::Context;
2use crate::dbs::{Options, Transaction};
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::sql::{
6 escape::escape_key,
7 fmt::{is_pretty, pretty_indent, Fmt, Pretty},
8 Operation, Thing, Value,
9};
10use revision::revisioned;
11use serde::{Deserialize, Serialize};
12use std::collections::BTreeMap;
13use std::collections::HashMap;
14use std::fmt::{self, Display, Formatter, Write};
15use std::ops::Deref;
16use std::ops::DerefMut;
17
18pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Object";
19
20#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
22#[serde(rename = "$surrealdb::private::sql::Object")]
23#[revisioned(revision = 1)]
24pub struct Object(#[serde(with = "no_nul_bytes_in_keys")] pub BTreeMap<String, Value>);
25
26impl From<BTreeMap<String, Value>> for Object {
27 fn from(v: BTreeMap<String, Value>) -> Self {
28 Self(v)
29 }
30}
31
32impl From<HashMap<&str, Value>> for Object {
33 fn from(v: HashMap<&str, Value>) -> Self {
34 Self(v.into_iter().map(|(key, val)| (key.to_string(), val)).collect())
35 }
36}
37
38impl From<HashMap<String, Value>> for Object {
39 fn from(v: HashMap<String, Value>) -> Self {
40 Self(v.into_iter().collect())
41 }
42}
43
44impl From<Option<Self>> for Object {
45 fn from(v: Option<Self>) -> Self {
46 v.unwrap_or_default()
47 }
48}
49
50impl From<Operation> for Object {
51 fn from(v: Operation) -> Self {
52 Self(match v {
53 Operation::Add {
54 path,
55 value,
56 } => map! {
57 String::from("op") => Value::from("add"),
58 String::from("path") => path.to_path().into(),
59 String::from("value") => value
60 },
61 Operation::Remove {
62 path,
63 } => map! {
64 String::from("op") => Value::from("remove"),
65 String::from("path") => path.to_path().into()
66 },
67 Operation::Replace {
68 path,
69 value,
70 } => map! {
71 String::from("op") => Value::from("replace"),
72 String::from("path") => path.to_path().into(),
73 String::from("value") => value
74 },
75 Operation::Change {
76 path,
77 value,
78 } => map! {
79 String::from("op") => Value::from("change"),
80 String::from("path") => path.to_path().into(),
81 String::from("value") => value
82 },
83 Operation::Copy {
84 path,
85 from,
86 } => map! {
87 String::from("op") => Value::from("copy"),
88 String::from("path") => path.to_path().into(),
89 String::from("from") => from.to_path().into()
90 },
91 Operation::Move {
92 path,
93 from,
94 } => map! {
95 String::from("op") => Value::from("move"),
96 String::from("path") => path.to_path().into(),
97 String::from("from") => from.to_path().into()
98 },
99 Operation::Test {
100 path,
101 value,
102 } => map! {
103 String::from("op") => Value::from("test"),
104 String::from("path") => path.to_path().into(),
105 String::from("value") => value
106 },
107 })
108 }
109}
110
111impl Deref for Object {
112 type Target = BTreeMap<String, Value>;
113 fn deref(&self) -> &Self::Target {
114 &self.0
115 }
116}
117
118impl DerefMut for Object {
119 fn deref_mut(&mut self) -> &mut Self::Target {
120 &mut self.0
121 }
122}
123
124impl IntoIterator for Object {
125 type Item = (String, Value);
126 type IntoIter = std::collections::btree_map::IntoIter<String, Value>;
127 fn into_iter(self) -> Self::IntoIter {
128 self.0.into_iter()
129 }
130}
131
132impl Object {
133 pub fn rid(&self) -> Option<Thing> {
135 match self.get("id") {
136 Some(Value::Thing(v)) => Some(v.clone()),
137 _ => None,
138 }
139 }
140 pub fn to_operation(&self) -> Result<Operation, Error> {
142 match self.get("op") {
143 Some(op_val) => match self.get("path") {
144 Some(path_val) => {
145 let path = path_val.jsonpath();
146
147 let from =
148 self.get("from").map(|value| value.jsonpath()).ok_or(Error::InvalidPatch {
149 message: String::from("'from' key missing"),
150 });
151
152 let value = self.get("value").cloned().ok_or(Error::InvalidPatch {
153 message: String::from("'value' key missing"),
154 });
155
156 match op_val.clone().as_string().as_str() {
157 "add" => Ok(Operation::Add {
159 path,
160 value: value?,
161 }),
162 "remove" => Ok(Operation::Remove {
164 path,
165 }),
166 "replace" => Ok(Operation::Replace {
168 path,
169 value: value?,
170 }),
171 "change" => Ok(Operation::Change {
173 path,
174 value: value?,
175 }),
176 "copy" => Ok(Operation::Copy {
178 path,
179 from: from?,
180 }),
181 "move" => Ok(Operation::Move {
183 path,
184 from: from?,
185 }),
186 "test" => Ok(Operation::Test {
188 path,
189 value: value?,
190 }),
191 unknown_op => Err(Error::InvalidPatch {
192 message: format!("unknown op '{unknown_op}'"),
193 }),
194 }
195 }
196 _ => Err(Error::InvalidPatch {
197 message: String::from("'path' key missing"),
198 }),
199 },
200 _ => Err(Error::InvalidPatch {
201 message: String::from("'op' key missing"),
202 }),
203 }
204 }
205}
206
207impl Object {
208 pub(crate) async fn compute(
210 &self,
211 ctx: &Context<'_>,
212 opt: &Options,
213 txn: &Transaction,
214 doc: Option<&CursorDoc<'_>>,
215 ) -> Result<Value, Error> {
216 let mut x = BTreeMap::new();
217 for (k, v) in self.iter() {
218 match v.compute(ctx, opt, txn, doc).await {
219 Ok(v) => x.insert(k.clone(), v),
220 Err(e) => return Err(e),
221 };
222 }
223 Ok(Value::Object(Object(x)))
224 }
225}
226
227impl Display for Object {
228 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
229 let mut f = Pretty::from(f);
230 if is_pretty() {
231 f.write_char('{')?;
232 } else {
233 f.write_str("{ ")?;
234 }
235 if !self.is_empty() {
236 let indent = pretty_indent();
237 write!(
238 f,
239 "{}",
240 Fmt::pretty_comma_separated(
241 self.0.iter().map(|args| Fmt::new(args, |(k, v), f| write!(
242 f,
243 "{}: {}",
244 escape_key(k),
245 v
246 ))),
247 )
248 )?;
249 drop(indent);
250 }
251 if is_pretty() {
252 f.write_char('}')
253 } else {
254 f.write_str(" }")
255 }
256 }
257}
258
259mod no_nul_bytes_in_keys {
260 use serde::{
261 de::{self, Visitor},
262 ser::SerializeMap,
263 Deserializer, Serializer,
264 };
265 use std::{collections::BTreeMap, fmt};
266
267 use crate::sql::Value;
268
269 pub(crate) fn serialize<S>(
270 m: &BTreeMap<String, Value>,
271 serializer: S,
272 ) -> Result<S::Ok, S::Error>
273 where
274 S: Serializer,
275 {
276 let mut s = serializer.serialize_map(Some(m.len()))?;
277 for (k, v) in m {
278 debug_assert!(!k.contains('\0'));
279 s.serialize_entry(k, v)?;
280 }
281 s.end()
282 }
283
284 pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<BTreeMap<String, Value>, D::Error>
285 where
286 D: Deserializer<'de>,
287 {
288 struct NoNulBytesInKeysVisitor;
289
290 impl<'de> Visitor<'de> for NoNulBytesInKeysVisitor {
291 type Value = BTreeMap<String, Value>;
292
293 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
294 formatter.write_str("a map without any NUL bytes in its keys")
295 }
296
297 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
298 where
299 A: de::MapAccess<'de>,
300 {
301 let mut ret = BTreeMap::new();
302 while let Some((k, v)) = map.next_entry()? {
303 ret.insert(k, v);
304 }
305 Ok(ret)
306 }
307 }
308
309 deserializer.deserialize_map(NoNulBytesInKeysVisitor)
310 }
311}