1use crate::ctx::Context;
2use crate::dbs::Options;
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 http::{HeaderMap, HeaderName, HeaderValue};
11use reblessive::tree::Stk;
12use revision::revisioned;
13use serde::{Deserialize, Serialize};
14use std::collections::BTreeMap;
15use std::collections::HashMap;
16use std::fmt::{self, Display, Formatter, Write};
17use std::ops::Deref;
18use std::ops::DerefMut;
19
20pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Object";
21
22#[revisioned(revision = 1)]
24#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
25#[serde(rename = "$surrealdb::private::sql::Object")]
26#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
27#[non_exhaustive]
28pub struct Object(#[serde(with = "no_nul_bytes_in_keys")] pub BTreeMap<String, Value>);
29
30impl From<BTreeMap<&str, Value>> for Object {
31 fn from(v: BTreeMap<&str, Value>) -> Self {
32 Self(v.into_iter().map(|(key, val)| (key.to_string(), val)).collect())
33 }
34}
35
36impl From<BTreeMap<String, Value>> for Object {
37 fn from(v: BTreeMap<String, Value>) -> Self {
38 Self(v)
39 }
40}
41
42impl FromIterator<(String, Value)> for Object {
43 fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
44 Self(BTreeMap::from_iter(iter))
45 }
46}
47
48impl From<BTreeMap<String, String>> for Object {
49 fn from(v: BTreeMap<String, String>) -> Self {
50 Self(v.into_iter().map(|(k, v)| (k, Value::from(v))).collect())
51 }
52}
53
54impl TryFrom<HeaderMap> for Object {
55 type Error = Error;
56 fn try_from(v: HeaderMap) -> Result<Self, Error> {
57 Ok(Self(
58 v.into_iter()
59 .map(|(k, v)| {
60 if let Some(k) = k {
61 Ok((k.to_string(), Value::from(v.to_str()?)))
62 } else {
63 Err(Error::Unreachable("Encountered a header without a name".into()))
64 }
65 })
66 .collect::<Result<BTreeMap<String, Value>, Error>>()?,
67 ))
68 }
69}
70
71impl From<HashMap<&str, Value>> for Object {
72 fn from(v: HashMap<&str, Value>) -> Self {
73 Self(v.into_iter().map(|(key, val)| (key.to_string(), val)).collect())
74 }
75}
76
77impl From<HashMap<String, Value>> for Object {
78 fn from(v: HashMap<String, Value>) -> Self {
79 Self(v.into_iter().collect())
80 }
81}
82
83impl From<Option<Self>> for Object {
84 fn from(v: Option<Self>) -> Self {
85 v.unwrap_or_default()
86 }
87}
88
89impl From<Operation> for Object {
90 fn from(v: Operation) -> Self {
91 Self(match v {
92 Operation::Add {
93 path,
94 value,
95 } => map! {
96 String::from("op") => Value::from("add"),
97 String::from("path") => path.to_path().into(),
98 String::from("value") => value
99 },
100 Operation::Remove {
101 path,
102 } => map! {
103 String::from("op") => Value::from("remove"),
104 String::from("path") => path.to_path().into()
105 },
106 Operation::Replace {
107 path,
108 value,
109 } => map! {
110 String::from("op") => Value::from("replace"),
111 String::from("path") => path.to_path().into(),
112 String::from("value") => value
113 },
114 Operation::Change {
115 path,
116 value,
117 } => map! {
118 String::from("op") => Value::from("change"),
119 String::from("path") => path.to_path().into(),
120 String::from("value") => value
121 },
122 Operation::Copy {
123 path,
124 from,
125 } => map! {
126 String::from("op") => Value::from("copy"),
127 String::from("path") => path.to_path().into(),
128 String::from("from") => from.to_path().into()
129 },
130 Operation::Move {
131 path,
132 from,
133 } => map! {
134 String::from("op") => Value::from("move"),
135 String::from("path") => path.to_path().into(),
136 String::from("from") => from.to_path().into()
137 },
138 Operation::Test {
139 path,
140 value,
141 } => map! {
142 String::from("op") => Value::from("test"),
143 String::from("path") => path.to_path().into(),
144 String::from("value") => value
145 },
146 })
147 }
148}
149
150impl Deref for Object {
151 type Target = BTreeMap<String, Value>;
152 fn deref(&self) -> &Self::Target {
153 &self.0
154 }
155}
156
157impl DerefMut for Object {
158 fn deref_mut(&mut self) -> &mut Self::Target {
159 &mut self.0
160 }
161}
162
163impl IntoIterator for Object {
164 type Item = (String, Value);
165 type IntoIter = std::collections::btree_map::IntoIter<String, Value>;
166 fn into_iter(self) -> Self::IntoIter {
167 self.0.into_iter()
168 }
169}
170
171impl TryInto<BTreeMap<String, String>> for Object {
172 type Error = Error;
173 fn try_into(self) -> Result<BTreeMap<String, String>, Self::Error> {
174 self.into_iter().map(|(k, v)| Ok((k, v.coerce_to_string()?))).collect()
175 }
176}
177
178impl TryInto<HeaderMap> for Object {
179 type Error = Error;
180 fn try_into(self) -> Result<HeaderMap, Self::Error> {
181 let mut headermap = HeaderMap::new();
182 for (k, v) in self.into_iter() {
183 let k: HeaderName = k.parse()?;
184 let v: HeaderValue = v.coerce_to_string()?.parse()?;
185 headermap.insert(k, v);
186 }
187
188 Ok(headermap)
189 }
190}
191
192impl Object {
193 pub fn rid(&self) -> Option<Thing> {
195 match self.get("id") {
196 Some(Value::Thing(v)) => Some(v.clone()),
197 _ => None,
198 }
199 }
200 pub fn to_operation(&self) -> Result<Operation, Error> {
202 match self.get("op") {
203 Some(op_val) => match self.get("path") {
204 Some(path_val) => {
205 let path = path_val.jsonpath();
206
207 let from =
208 self.get("from").map(|value| value.jsonpath()).ok_or(Error::InvalidPatch {
209 message: String::from("'from' key missing"),
210 });
211
212 let value = self.get("value").cloned().ok_or(Error::InvalidPatch {
213 message: String::from("'value' key missing"),
214 });
215
216 match op_val.clone().as_string().as_str() {
217 "add" => Ok(Operation::Add {
219 path,
220 value: value?,
221 }),
222 "remove" => Ok(Operation::Remove {
224 path,
225 }),
226 "replace" => Ok(Operation::Replace {
228 path,
229 value: value?,
230 }),
231 "change" => Ok(Operation::Change {
233 path,
234 value: value?,
235 }),
236 "copy" => Ok(Operation::Copy {
238 path,
239 from: from?,
240 }),
241 "move" => Ok(Operation::Move {
243 path,
244 from: from?,
245 }),
246 "test" => Ok(Operation::Test {
248 path,
249 value: value?,
250 }),
251 unknown_op => Err(Error::InvalidPatch {
252 message: format!("unknown op '{unknown_op}'"),
253 }),
254 }
255 }
256 _ => Err(Error::InvalidPatch {
257 message: String::from("'path' key missing"),
258 }),
259 },
260 _ => Err(Error::InvalidPatch {
261 message: String::from("'op' key missing"),
262 }),
263 }
264 }
265}
266
267impl Object {
268 pub(crate) async fn compute(
270 &self,
271 stk: &mut Stk,
272 ctx: &Context,
273 opt: &Options,
274 doc: Option<&CursorDoc>,
275 ) -> Result<Value, Error> {
276 let mut x = BTreeMap::new();
277 for (k, v) in self.iter() {
278 match v.compute(stk, ctx, opt, doc).await {
279 Ok(v) => x.insert(k.clone(), v),
280 Err(e) => return Err(e),
281 };
282 }
283 Ok(Value::Object(Object(x)))
284 }
285
286 pub(crate) fn is_static(&self) -> bool {
288 self.values().all(Value::is_static)
289 }
290
291 pub(crate) fn validate_computed(&self) -> Result<(), Error> {
293 self.values().try_for_each(|v| v.validate_computed())
294 }
295}
296
297impl Display for Object {
298 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
299 let mut f = Pretty::from(f);
300 if is_pretty() {
301 f.write_char('{')?;
302 } else {
303 f.write_str("{ ")?;
304 }
305 if !self.is_empty() {
306 let indent = pretty_indent();
307 write!(
308 f,
309 "{}",
310 Fmt::pretty_comma_separated(
311 self.0.iter().map(|args| Fmt::new(args, |(k, v), f| write!(
312 f,
313 "{}: {}",
314 escape_key(k),
315 v
316 ))),
317 )
318 )?;
319 drop(indent);
320 }
321 if is_pretty() {
322 f.write_char('}')
323 } else {
324 f.write_str(" }")
325 }
326 }
327}
328
329mod no_nul_bytes_in_keys {
330 use serde::{
331 de::{self, Visitor},
332 ser::SerializeMap,
333 Deserializer, Serializer,
334 };
335 use std::{collections::BTreeMap, fmt};
336
337 use crate::sql::Value;
338
339 pub(crate) fn serialize<S>(
340 m: &BTreeMap<String, Value>,
341 serializer: S,
342 ) -> Result<S::Ok, S::Error>
343 where
344 S: Serializer,
345 {
346 let mut s = serializer.serialize_map(Some(m.len()))?;
347 for (k, v) in m {
348 debug_assert!(!k.contains('\0'));
349 s.serialize_entry(k, v)?;
350 }
351 s.end()
352 }
353
354 pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<BTreeMap<String, Value>, D::Error>
355 where
356 D: Deserializer<'de>,
357 {
358 struct NoNulBytesInKeysVisitor;
359
360 impl<'de> Visitor<'de> for NoNulBytesInKeysVisitor {
361 type Value = BTreeMap<String, Value>;
362
363 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
364 formatter.write_str("a map without any NUL bytes in its keys")
365 }
366
367 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
368 where
369 A: de::MapAccess<'de>,
370 {
371 let mut ret = BTreeMap::new();
372 while let Some((k, v)) = map.next_entry()? {
373 ret.insert(k, v);
374 }
375 Ok(ret)
376 }
377 }
378
379 deserializer.deserialize_map(NoNulBytesInKeysVisitor)
380 }
381}