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