Skip to main content

arete_server/
shared_entity.rs

1//! Cached entities that share their fields between the views holding them.
2//!
3//! Every view of an export receives the same patches, and a derived view holds
4//! the entity its source view holds, so most cached copies of one entity have
5//! the same fields. Each view's frames carry their own `_version` (see the
6//! projector), which a view's copy keeps, so the copies differ in that one
7//! field. A [`SharedEntity`] keeps every other field behind an [`Arc`] that the
8//! copies share, and `_version` beside it, per copy. A change replaces or
9//! copies the shared fields before it writes (copy-on-write), so it never
10//! reaches another holder's copy.
11//!
12//! The shared fields of an object are themselves shared one top-level field
13//! at a time (see [`Fields`]): copying them before a write copies a list of
14//! pointers, and the write then copies only the fields it changes. A patch to
15//! one section of a large entity costs that section, not the entity, and the
16//! old version, still held by a derived view or a subscriber, shares every
17//! other section with the new one. Dropping the old version frees only what
18//! the patch replaced.
19
20use 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
28/// The field each view's copy of an entity stamps with its own frame version.
29pub const VERSION_FIELD: &str = "_version";
30
31/// One copy of a cached entity: fields shared with every other copy that has
32/// the same ones, plus this copy's `_version`.
33///
34/// It reads and compares as the whole entity it stands for, `_version`
35/// included: [`Self::to_value`] rebuilds that entity, and equality, field
36/// access ([`EntityFields`]) and indexing see it too. It serializes exactly as
37/// that entity does, without rebuilding it.
38#[derive(Clone, Debug)]
39pub struct SharedEntity {
40    /// Every field but `_version`. Never has a top-level `_version`.
41    fields: Arc<Fields>,
42    /// `_version`, if the entity is an object that has one.
43    version: Option<Value>,
44}
45
46impl SharedEntity {
47    /// Hold `entity` unshared.
48    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    /// Hold `fields`, shared with whoever else holds them, under `version`.
57    ///
58    /// `fields` must not have a top-level `_version`, and only an object
59    /// carries one.
60    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    /// The entity's `_version`, if it has one.
89    pub fn version(&self) -> Option<&Value> {
90        self.version.as_ref()
91    }
92
93    /// Whether `other` shares this entity's fields rather than holding a copy
94    /// of its own. Sharing says nothing about `_version`.
95    pub fn shares_fields_with(&self, other: &SharedEntity) -> bool {
96        Arc::ptr_eq(&self.fields, &other.fields)
97    }
98
99    /// The whole entity, as an owned value.
100    pub fn to_value(&self) -> Value {
101        with_version(self.fields.to_value(), self.version.clone())
102    }
103
104    /// The whole entity, copying the fields only if another holder shares
105    /// them.
106    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
127/// Equal when the whole entities are: `_version` and every other field.
128impl 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
135/// Serializes exactly as [`SharedEntity::to_value`] would, in any format,
136/// without building that value: `_version` is written where rebuilding the
137/// entity would have put it.
138impl 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    /// Serialize the entity as [`Serialize`] does, passing each top-level
146    /// field's value (`_version` included) through `field`, or the whole
147    /// entity through `other` if it is not an object.
148    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/// The fields of a [`SharedEntity`]: every one but `_version`.
188///
189/// An object keeps each top-level field behind an [`Arc`] of its own, so a
190/// copy of the object shares every field with the original and a write to it
191/// copies only the field it changes ([`Arc::make_mut`] on the [`Fields`]
192/// copies the pointers, on a [`Members`] entry that one field). Anything else
193/// is held whole.
194#[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    /// The fields as one value, copying them.
209    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    /// The fields as one value, copying only those another holder shares.
222    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    /// The top-level field `name`, if these are an object's fields.
240    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
252/// Equal when the values they stand for are.
253impl 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/// An object's top-level fields, each shared on its own, in the order
264/// `serde_json` keeps an object's keys (see [`maps_sort_keys`]): sorted by
265/// key, or in insertion order.
266#[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        // A map iterates in the order kept here.
272        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    /// Set `name` to `value`, where it is if it is there already, otherwise
299    /// where a `serde_json` object would put a new key.
300    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
316/// Equal as `serde_json` objects are: the same keys with equal values, in
317/// any order.
318impl 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
329/// Whether `serde_json` keeps an object's keys sorted, its default, rather
330/// than in insertion order (its `preserve_order` feature). Rebuilding an
331/// entity inserts `_version` last, which lands it in key order in the first
332/// case and at the end in the second.
333pub(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
343/// Reads a field the way indexing a [`Value`] does: `Null` when it is absent.
344impl 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
353/// Field access shared by plain values and [`SharedEntity`] copies.
354pub trait EntityFields {
355    /// The top-level field `name`, if the entity is an object that has it.
356    fn field(&self, name: &str) -> Option<&Value>;
357
358    /// The whole entity. Borrowed whenever it can be.
359    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
389/// The value at `path`, field names from the top of `entity`; the whole entity
390/// for an empty path. `None` as soon as a step is missing.
391pub 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
408/// Take `_version` out of `value`, if it is an object that has one.
409pub(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
417/// Put `version` back into `fields` as `_version`. Only an object takes one.
418pub(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    /// Serializing a copy writes the bytes the whole entity would, wherever
468    /// `_version` falls among the other fields.
469    #[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}