1use std::borrow::Cow;
21use std::ops::Index;
22use std::sync::{Arc, OnceLock};
23
24use serde::ser::SerializeMap as _;
25use serde::{Deserialize, Deserializer, Serialize, Serializer};
26use serde_json::Value;
27
28pub const VERSION_FIELD: &str = "_version";
30
31#[derive(Clone, Debug)]
39pub struct SharedEntity {
40 fields: Arc<Fields>,
42 version: Option<Value>,
44}
45
46impl SharedEntity {
47 pub fn new(entity: Value) -> Self {
49 let (fields, version) = split_version(entity);
50 Self {
51 fields: Arc::new(Fields::from_value(fields)),
52 version,
53 }
54 }
55
56 pub(crate) fn from_parts(fields: Arc<Fields>, version: Option<Value>) -> Self {
61 debug_assert!(
62 fields.get(VERSION_FIELD).is_none(),
63 "shared fields must not carry `_version`"
64 );
65 debug_assert!(
66 fields.is_object() || version.is_none(),
67 "only an object carries `_version`"
68 );
69 Self { fields, version }
70 }
71
72 pub(crate) fn into_parts(self) -> (Arc<Fields>, Option<Value>) {
73 (self.fields, self.version)
74 }
75
76 pub(crate) fn fields(&self) -> &Arc<Fields> {
77 &self.fields
78 }
79
80 pub(crate) fn fields_mut(&mut self) -> &mut Arc<Fields> {
81 &mut self.fields
82 }
83
84 pub(crate) fn version_mut(&mut self) -> &mut Option<Value> {
85 &mut self.version
86 }
87
88 pub fn version(&self) -> Option<&Value> {
90 self.version.as_ref()
91 }
92
93 pub fn shares_fields_with(&self, other: &SharedEntity) -> bool {
96 Arc::ptr_eq(&self.fields, &other.fields)
97 }
98
99 pub fn to_value(&self) -> Value {
101 with_version(self.fields.to_value(), self.version.clone())
102 }
103
104 pub fn into_value(self) -> Value {
107 let fields = match Arc::try_unwrap(self.fields) {
108 Ok(fields) => fields.into_value(),
109 Err(shared) => shared.to_value(),
110 };
111 with_version(fields, self.version)
112 }
113}
114
115impl From<Value> for SharedEntity {
116 fn from(entity: Value) -> Self {
117 Self::new(entity)
118 }
119}
120
121impl From<SharedEntity> for Value {
122 fn from(entity: SharedEntity) -> Self {
123 entity.into_value()
124 }
125}
126
127impl PartialEq for SharedEntity {
129 fn eq(&self, other: &Self) -> bool {
130 self.version == other.version
131 && (Arc::ptr_eq(&self.fields, &other.fields) || self.fields == other.fields)
132 }
133}
134
135impl Serialize for SharedEntity {
139 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
140 self.serialize_with(serializer, |_, value| value, |value| value)
141 }
142}
143
144impl SharedEntity {
145 pub(crate) fn serialize_with<'a, S, F, O>(
149 &'a self,
150 serializer: S,
151 field: impl Fn(&'a str, &'a Value) -> F,
152 other: impl FnOnce(&'a Value) -> O,
153 ) -> Result<S::Ok, S::Error>
154 where
155 S: Serializer,
156 F: Serialize,
157 O: Serialize,
158 {
159 let members = match &*self.fields {
160 Fields::Object(members) => members,
161 Fields::Other(value) => return other(value).serialize(serializer),
162 };
163 let mut map =
164 serializer.serialize_map(Some(members.len() + usize::from(self.version.is_some())))?;
165 let mut pending = self.version.as_ref();
166 for (key, value) in members.iter() {
167 if maps_sort_keys() && key.as_str() > VERSION_FIELD {
168 if let Some(version) = pending.take() {
169 map.serialize_entry(VERSION_FIELD, &field(VERSION_FIELD, version))?;
170 }
171 }
172 map.serialize_entry(key, &field(key, value))?;
173 }
174 if let Some(version) = pending {
175 map.serialize_entry(VERSION_FIELD, &field(VERSION_FIELD, version))?;
176 }
177 map.end()
178 }
179}
180
181impl<'de> Deserialize<'de> for SharedEntity {
182 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
183 Value::deserialize(deserializer).map(SharedEntity::new)
184 }
185}
186
187#[derive(Clone, Debug)]
195pub(crate) enum Fields {
196 Object(Members),
197 Other(Value),
198}
199
200impl Fields {
201 pub(crate) fn from_value(value: Value) -> Self {
202 match value {
203 Value::Object(map) => Fields::Object(Members::from_map(map)),
204 other => Fields::Other(other),
205 }
206 }
207
208 pub(crate) fn to_value(&self) -> Value {
210 match self {
211 Fields::Object(members) => Value::Object(
212 members
213 .iter()
214 .map(|(key, value)| (key.clone(), Value::clone(value)))
215 .collect(),
216 ),
217 Fields::Other(value) => value.clone(),
218 }
219 }
220
221 pub(crate) fn into_value(self) -> Value {
223 match self {
224 Fields::Object(members) => Value::Object(
225 members
226 .0
227 .into_iter()
228 .map(|(key, value)| {
229 let value =
230 Arc::try_unwrap(value).unwrap_or_else(|shared| (*shared).clone());
231 (key, value)
232 })
233 .collect(),
234 ),
235 Fields::Other(value) => value,
236 }
237 }
238
239 pub(crate) fn get(&self, name: &str) -> Option<&Value> {
241 match self {
242 Fields::Object(members) => members.get(name).map(|value| &**value),
243 Fields::Other(_) => None,
244 }
245 }
246
247 pub(crate) fn is_object(&self) -> bool {
248 matches!(self, Fields::Object(_))
249 }
250}
251
252impl PartialEq for Fields {
254 fn eq(&self, other: &Self) -> bool {
255 match (self, other) {
256 (Fields::Object(left), Fields::Object(right)) => left == right,
257 (Fields::Other(left), Fields::Other(right)) => left == right,
258 _ => false,
259 }
260 }
261}
262
263#[derive(Clone, Debug, Default)]
267pub(crate) struct Members(Vec<(String, Arc<Value>)>);
268
269impl Members {
270 fn from_map(map: serde_json::Map<String, Value>) -> Self {
271 Members(
273 map.into_iter()
274 .map(|(key, value)| (key, Arc::new(value)))
275 .collect(),
276 )
277 }
278
279 fn position(&self, name: &str) -> Result<usize, usize> {
280 if maps_sort_keys() {
281 self.0.binary_search_by(|(key, _)| key.as_str().cmp(name))
282 } else {
283 self.0
284 .iter()
285 .position(|(key, _)| key == name)
286 .ok_or(self.0.len())
287 }
288 }
289
290 pub(crate) fn get(&self, name: &str) -> Option<&Arc<Value>> {
291 self.position(name).ok().map(|index| &self.0[index].1)
292 }
293
294 pub(crate) fn get_mut(&mut self, name: &str) -> Option<&mut Arc<Value>> {
295 self.position(name).ok().map(|index| &mut self.0[index].1)
296 }
297
298 pub(crate) fn insert(&mut self, name: String, value: Arc<Value>) {
301 match self.position(&name) {
302 Ok(index) => self.0[index].1 = value,
303 Err(index) => self.0.insert(index, (name, value)),
304 }
305 }
306
307 pub(crate) fn iter(&self) -> impl Iterator<Item = (&String, &Arc<Value>)> {
308 self.0.iter().map(|(key, value)| (key, value))
309 }
310
311 pub(crate) fn len(&self) -> usize {
312 self.0.len()
313 }
314}
315
316impl PartialEq for Members {
319 fn eq(&self, other: &Self) -> bool {
320 self.len() == other.len()
321 && self.iter().all(|(key, value)| {
322 other
323 .get(key)
324 .is_some_and(|theirs| Arc::ptr_eq(value, theirs) || value == theirs)
325 })
326 }
327}
328
329pub(crate) fn maps_sort_keys() -> bool {
334 static SORTED: OnceLock<bool> = OnceLock::new();
335 *SORTED.get_or_init(|| {
336 let mut map = serde_json::Map::new();
337 map.insert("b".to_string(), Value::Null);
338 map.insert("a".to_string(), Value::Null);
339 map.keys().next().map(String::as_str) == Some("a")
340 })
341}
342
343impl Index<&str> for SharedEntity {
345 type Output = Value;
346
347 fn index(&self, field: &str) -> &Value {
348 static NULL: Value = Value::Null;
349 self.field(field).unwrap_or(&NULL)
350 }
351}
352
353pub trait EntityFields {
355 fn field(&self, name: &str) -> Option<&Value>;
357
358 fn whole(&self) -> Cow<'_, Value>;
360}
361
362impl EntityFields for Value {
363 fn field(&self, name: &str) -> Option<&Value> {
364 self.get(name)
365 }
366
367 fn whole(&self) -> Cow<'_, Value> {
368 Cow::Borrowed(self)
369 }
370}
371
372impl EntityFields for SharedEntity {
373 fn field(&self, name: &str) -> Option<&Value> {
374 if name == VERSION_FIELD {
375 self.version.as_ref()
376 } else {
377 self.fields.get(name)
378 }
379 }
380
381 fn whole(&self) -> Cow<'_, Value> {
382 match (&*self.fields, &self.version) {
383 (Fields::Other(value), None) => Cow::Borrowed(value),
384 _ => Cow::Owned(self.to_value()),
385 }
386 }
387}
388
389pub fn lookup<'a, E, I, S>(entity: &'a E, path: I) -> Option<Cow<'a, Value>>
392where
393 E: EntityFields + ?Sized,
394 I: IntoIterator<Item = S>,
395 S: AsRef<str>,
396{
397 let mut path = path.into_iter();
398 let Some(first) = path.next() else {
399 return Some(entity.whole());
400 };
401 let mut current = entity.field(first.as_ref())?;
402 for segment in path {
403 current = current.get(segment.as_ref())?;
404 }
405 Some(Cow::Borrowed(current))
406}
407
408pub(crate) fn split_version(mut value: Value) -> (Value, Option<Value>) {
410 let version = match &mut value {
411 Value::Object(map) => map.remove(VERSION_FIELD),
412 _ => None,
413 };
414 (value, version)
415}
416
417pub(crate) fn with_version(mut fields: Value, version: Option<Value>) -> Value {
419 if let (Value::Object(map), Some(version)) = (&mut fields, version) {
420 map.insert(VERSION_FIELD.to_string(), version);
421 }
422 fields
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428 use serde_json::json;
429
430 #[test]
431 fn it_reads_compares_and_rebuilds_as_the_whole_entity() {
432 let whole = json!({"a": 1, "_seq": "1:0", "_version": "e:7", "nested": {"b": [1, 2]}});
433 let entity = SharedEntity::new(whole.clone());
434 assert_eq!(entity.version(), Some(&json!("e:7")));
435 assert!(entity.fields().get(VERSION_FIELD).is_none());
436 assert_eq!(entity.field("_version"), Some(&json!("e:7")));
437 assert_eq!(entity["a"], 1);
438 assert_eq!(entity["missing"], Value::Null);
439 assert_eq!(
440 lookup(&entity, ["nested", "b"]).as_deref(),
441 Some(&json!([1, 2]))
442 );
443 assert_eq!(
444 lookup(&entity, ["_version"]).as_deref(),
445 Some(&json!("e:7"))
446 );
447 assert_eq!(lookup(&entity, Vec::<&str>::new()).as_deref(), Some(&whole));
448 assert_eq!(entity.to_value(), whole);
449 assert_eq!(
450 serde_json::to_string(&entity.clone().into_value()).unwrap(),
451 serde_json::to_string(&whole).unwrap(),
452 "the rebuilt entity serializes byte for byte as the original"
453 );
454 assert_eq!(entity, SharedEntity::new(whole));
455 }
456
457 #[test]
458 fn copies_that_differ_only_in_version_are_not_equal() {
459 let list = SharedEntity::new(json!({"a": 1, "_version": "e:1"}));
460 let (fields, _) = list.clone().into_parts();
461 let state = SharedEntity::from_parts(fields, Some(json!("e:2")));
462 assert!(state.shares_fields_with(&list));
463 assert_ne!(state, list);
464 assert_eq!(state.to_value(), json!({"a": 1, "_version": "e:2"}));
465 }
466
467 #[test]
470 fn it_serializes_as_the_whole_entity() {
471 let entities = [
472 json!({"a": 1, "_version": "e:1"}),
473 json!({"_a": 1, "_seq": "1:0", "_version": "e:1", "_z": 2, "Z": 3, "z": [1, {"_version": 2}]}),
474 json!({"_version": "e:1"}),
475 json!({"a": 1}),
476 json!({"_version": {"nested": [1, 2]}, "b": null}),
477 json!([1, 2, {"_version": "kept in place"}]),
478 json!("text"),
479 json!(null),
480 ];
481 for whole in entities {
482 let entity = SharedEntity::new(whole.clone());
483 assert_eq!(
484 serde_json::to_string(&entity).unwrap(),
485 serde_json::to_string(&whole).unwrap(),
486 "{whole}"
487 );
488 let read: SharedEntity = serde_json::from_value(whole.clone()).unwrap();
489 assert_eq!(read, entity);
490 }
491 }
492
493 #[test]
494 fn a_value_that_is_not_an_object_has_no_version() {
495 let entity = SharedEntity::new(json!([1, 2, 3]));
496 assert_eq!(entity.version(), None);
497 assert_eq!(entity.field("_version"), None);
498 assert_eq!(entity.into_value(), json!([1, 2, 3]));
499 }
500}