oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

1use dashmap::{DashMap, Entry};
2use serde::de::DeserializeOwned;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::fmt::Formatter;
6use std::sync::Arc;
7use thiserror::Error;
8
9use crate::block::ClientID;
10use crate::sync::{Clock, Timestamp};
11use crate::updates::decoder::{Decode, Decoder};
12use crate::updates::encoder::{Encode, Encoder};
13use crate::{Doc, Observer, Origin};
14
15const NULL_STR: &str = "null";
16
17#[cfg(feature = "sync")]
18type AwarenessUpdateFn = Box<dyn Fn(&Awareness, &Event, Option<&Origin>) + Send + Sync + 'static>;
19
20#[cfg(not(feature = "sync"))]
21type AwarenessUpdateFn = Box<dyn Fn(&Awareness, &Event, Option<&Origin>) + 'static>;
22
23/// The Awareness class implements a simple shared state protocol that can be used for non-persistent
24/// data like awareness information (cursor, username, status, ..). Each client can update its own
25/// local state and listen to state changes of remote clients.
26///
27/// Each client is identified by a unique client id (something we borrow from `doc.clientID`).
28/// A client can override its own state by propagating a message with an increasing timestamp
29/// (`clock`). If such a message is received, it is applied if the known state of that client is
30/// older than the new state (`clock < new_clock`). If a client thinks that a remote client is
31/// offline, it may propagate a message with `{ clock, state: null, client }`. If such a message is
32/// received, and the known clock of that client equals the received clock, it will clean the state.
33///
34/// Before a client disconnects, it should propagate a `null` state with an updated clock.
35pub struct Awareness {
36 doc: Doc,
37 states: DashMap<ClientID, ClientState>,
38 clock: Arc<dyn Clock>,
39 on_update: Observer<AwarenessUpdateFn>,
40 on_change: Observer<AwarenessUpdateFn>,
41}
42
43impl Awareness {
44 /// Creates a new instance of [Awareness] struct, which operates over a given document.
45 /// Awareness instance has full ownership of that document. If necessary it can be accessed
46 /// using either [Awareness::doc] or [Awareness::doc_mut] methods.
47 #[cfg(not(target_family = "wasm"))]
48 pub fn new(doc: Doc) -> Self {
49 Self::with_clock(doc, crate::sync::time::SystemClock)
50 }
51
52 /// Creates a new instance of [Awareness] struct, which operates over a given document.
53 /// Awareness instance has full ownership of that document. If necessary it can be accessed
54 /// using either [Awareness::doc] or [Awareness::doc_mut] methods.
55 pub fn with_clock<C>(doc: Doc, clock: C) -> Self
56 where
57 C: Clock + 'static,
58 {
59 Awareness {
60 doc,
61 states: DashMap::new(),
62 clock: Arc::new(clock),
63 on_update: Observer::new(),
64 on_change: Observer::new(),
65 }
66 }
67
68 /// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
69 #[cfg(feature = "sync")]
70 pub fn on_update<F>(&self, f: F) -> crate::Subscription
71 where
72 F: Fn(&Awareness, &Event, Option<&Origin>) + Send + Sync + 'static,
73 {
74 self.on_update.subscribe(Box::new(f))
75 }
76
77 /// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
78 #[cfg(not(feature = "sync"))]
79 pub fn on_update<F>(&self, f: F) -> crate::Subscription
80 where
81 F: Fn(&Awareness, &Event, Option<&Origin>) + 'static,
82 {
83 self.on_update.subscribe(Box::new(f))
84 }
85
86 /// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
87 #[cfg(feature = "sync")]
88 pub fn on_update_with<K, F>(&self, key: K, f: F)
89 where
90 K: Into<Origin>,
91 F: Fn(&Awareness, &Event, Option<&Origin>) + Send + Sync + 'static,
92 {
93 self.on_update.subscribe_with(key.into(), Box::new(f))
94 }
95
96 /// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
97 #[cfg(not(feature = "sync"))]
98 pub fn on_update_with<K, F>(&self, key: K, f: F)
99 where
100 K: Into<Origin>,
101 F: Fn(&Awareness, &Event, Option<&Origin>) + 'static,
102 {
103 self.on_update.subscribe_with(key.into(), Box::new(f))
104 }
105
106 /// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
107 pub fn unobserve_update<K>(&self, key: K) -> bool
108 where
109 K: Into<Origin>,
110 {
111 self.on_update.unsubscribe(&key.into())
112 }
113
114 /// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
115 #[cfg(feature = "sync")]
116 pub fn on_change<F>(&self, f: F) -> crate::Subscription
117 where
118 F: Fn(&Awareness, &Event, Option<&Origin>) + Send + Sync + 'static,
119 {
120 self.on_change.subscribe(Box::new(f))
121 }
122
123 /// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
124 #[cfg(not(feature = "sync"))]
125 pub fn on_change<F>(&self, f: F) -> crate::Subscription
126 where
127 F: Fn(&Awareness, &Event, Option<&Origin>) + 'static,
128 {
129 self.on_change.subscribe(Box::new(f))
130 }
131
132 /// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
133 #[cfg(feature = "sync")]
134 pub fn on_change_with<K, F>(&self, key: K, f: F)
135 where
136 K: Into<Origin>,
137 F: Fn(&Awareness, &Event, Option<&Origin>) + Send + Sync + 'static,
138 {
139 self.on_change.subscribe_with(key.into(), Box::new(f))
140 }
141
142 /// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
143 #[cfg(not(feature = "sync"))]
144 pub fn on_change_with<K, F>(&self, key: K, f: F)
145 where
146 K: Into<Origin>,
147 F: Fn(&Awareness, &Event, Option<&Origin>) + 'static,
148 {
149 self.on_change.subscribe_with(key.into(), Box::new(f))
150 }
151
152 /// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
153 pub fn unobserve_change<K>(&self, key: K) -> bool
154 where
155 K: Into<Origin>,
156 {
157 self.on_change.unsubscribe(&key.into())
158 }
159
160 /// Returns a read-only reference to an underlying [Doc].
161 pub fn doc(&self) -> &Doc {
162 &self.doc
163 }
164
165 /// Returns a read-write reference to an underlying [Doc].
166 pub fn doc_mut(&mut self) -> &mut Doc {
167 &mut self.doc
168 }
169
170 /// Returns a globally unique client ID of an underlying [Doc].
171 pub fn client_id(&self) -> ClientID {
172 self.doc.client_id()
173 }
174
175 /// Returns a state map of all the clients tracked by current [Awareness] instance. Those
176 /// states are identified by their corresponding [ClientID]s. The associated state is
177 /// represented and replicated to other clients as a JSON string.
178 pub fn iter(&self) -> AwarenessIter {
179 AwarenessIter(self.states.iter())
180 }
181
182 /// Returns a JSON string state representation of a current [Awareness] instance.
183 pub fn local_state<S: DeserializeOwned>(&self) -> Option<S> {
184 let json_str = self.local_state_raw()?;
185 serde_json::from_str(&json_str).ok()
186 }
187
188 /// Returns a JSON string state representation of a current [Awareness] instance.
189 pub fn local_state_raw(&self) -> Option<Arc<str>> {
190 let e = self.states.get(&self.doc.client_id())?;
191 let json_str = e.value().clone().data?;
192 Some(json_str)
193 }
194
195 /// Clears out a state of a given client, effectively marking it as disconnected.
196 pub fn remove_state(&self, client_id: ClientID) {
197 let is_removed = match self.states.entry(client_id) {
198 Entry::Occupied(mut e) => {
199 let state = e.get_mut();
200 state.data = None;
201 state.clock += 1;
202 true
203 }
204 Entry::Vacant(e) => {
205 e.insert(ClientState::new(1, self.clock.now(), None));
206 false
207 }
208 };
209 if is_removed && self.on_update.has_subscribers() || self.on_change.has_subscribers() {
210 let e = Event::new(Vec::default(), Vec::default(), vec![client_id]);
211 self.on_change.trigger(|fun| fun(self, &e, None));
212 self.on_update.trigger(|fun| fun(self, &e, None));
213 }
214 }
215
216 /// Gets the state of a particular client.
217 pub fn state<D: DeserializeOwned>(&self, client_id: ClientID) -> Option<D> {
218 let state = self.states.get(&client_id)?;
219 let json = state.data.clone()?;
220 serde_json::from_str(&json).ok()
221 }
222
223 /// Get the `(sequence number, last updated timestamp)` metadata associated with particular client.
224 pub fn meta(&self, client_id: ClientID) -> Option<(u32, Timestamp)> {
225 let state = self.states.get(&client_id)?;
226 Some((state.clock, state.last_updated))
227 }
228
229 /// Clears out a state of a current client (see: [Awareness::client_id]),
230 /// effectively marking it as disconnected.
231 pub fn clean_local_state(&self) {
232 let client_id = self.doc.client_id();
233 self.remove_state(client_id);
234 }
235
236 /// Sets a current [Awareness] instance state to a corresponding JSON string. This state will
237 /// be replicated to other clients as part of the [AwarenessUpdate] and it will trigger an event
238 /// to be emitted if current instance was created using [Awareness::with_observer] method.
239 pub fn set_local_state<S: Serialize>(&self, state: S) -> Result<(), Error> {
240 let json = serde_json::to_string(&state)?;
241 self.set_local_state_raw(json);
242 Ok(())
243 }
244
245 /// Sets a current [Awareness] instance state to a corresponding JSON string. This state will
246 /// be replicated to other clients as part of the [AwarenessUpdate] and it will trigger an event
247 /// to be emitted if current instance was created using [Awareness::with_observer] method.
248 pub fn set_local_state_raw<S: Into<Arc<str>>>(&self, json: S) {
249 let client_id = self.doc.client_id();
250 let now = self.clock.now();
251 let json = json.into();
252 let prev = match self.states.entry(client_id) {
253 Entry::Occupied(mut e) => {
254 let state = e.get_mut();
255 state.last_updated = now;
256 state.clock += 1;
257 state.data.replace(json.clone())
258 }
259 Entry::Vacant(e) => {
260 e.insert(ClientState::new(1, now, Some(json.clone())));
261 None
262 }
263 };
264 self.notify_subscribers(client_id, prev, json);
265 }
266
267 fn notify_subscribers(
268 &self,
269 client_id: ClientID,
270 prev_state: Option<Arc<str>>,
271 curr_state: Arc<str>,
272 ) {
273 if self.on_update.has_subscribers() || self.on_change.has_subscribers() {
274 let mut added = vec![];
275 let mut updated = vec![];
276 let mut changed = vec![];
277 match prev_state {
278 None => added.push(client_id),
279 Some(prev) => {
280 updated.push(client_id);
281 if prev != curr_state {
282 changed.push(client_id);
283 }
284 }
285 }
286 let mut e = Event::new(added, changed, Vec::default());
287 if !e.is_empty() {
288 self.on_change.trigger(|fun| fun(self, &e, None));
289 }
290 e.summary.updated = updated;
291 if !e.is_empty() {
292 self.on_update.trigger(|fun| fun(self, &e, None));
293 }
294 }
295 }
296
297 /// Returns a serializable update object which is representation of a current Awareness state.
298 /// This doesn't include states that have been removed in the past.
299 pub fn update(&self) -> Result<AwarenessUpdate, Error> {
300 let clients: Vec<_> = self
301 .states
302 .iter()
303 .flat_map(|e| {
304 if e.data.is_none() {
305 None
306 } else {
307 Some(*e.key())
308 }
309 })
310 .collect();
311 self.update_with_clients(clients)
312 }
313
314 /// Returns a serializable update object which is representation of a current Awareness state.
315 /// Unlike [Awareness::update], this method variant allows to prepare update only for a subset
316 /// of known clients. These clients must all be known to a current [Awareness] instance,
317 /// otherwise a [Error::ClientNotFound] error will be returned.
318 pub fn update_with_clients<I: IntoIterator<Item = ClientID>>(
319 &self,
320 clients: I,
321 ) -> Result<AwarenessUpdate, Error> {
322 let mut res = HashMap::new();
323 for client_id in clients {
324 let (clock, json) = if let Some(meta) = self.states.get(&client_id) {
325 let data = meta.data.clone().unwrap_or_else(|| NULL_STR.into());
326 (meta.clock, data)
327 } else {
328 return Err(Error::ClientNotFound(client_id));
329 };
330 res.insert(client_id, AwarenessUpdateEntry { clock, json });
331 }
332 Ok(AwarenessUpdate { clients: res })
333 }
334
335 /// Applies an update (incoming from remote channel or generated using [Awareness::update] /
336 /// [Awareness::update_with_clients] methods) and modifies a state of a current instance.
337 ///
338 /// If current instance has an observer channel (see: [Awareness::with_observer]), applied
339 /// changes will also be emitted as events.
340 pub fn apply_update(&self, update: AwarenessUpdate) -> Result<(), Error> {
341 let gen_summary = self.on_update.has_subscribers() || self.on_change.has_subscribers();
342 self.apply_update_internal(update, None, gen_summary)?;
343 Ok(())
344 }
345
346 /// Applies an update (incoming from remote channel or generated using [Awareness::update] /
347 /// [Awareness::update_with_clients] methods) and modifies a state of a current instance.
348 ///
349 /// If current instance has an observer channel (see: [Awareness::with_observer]), applied
350 /// changes will also be emitted as events.
351 pub fn apply_update_with<O: Into<Origin>>(
352 &self,
353 update: AwarenessUpdate,
354 origin: O,
355 ) -> Result<(), Error> {
356 let gen_summary = self.on_update.has_subscribers() || self.on_change.has_subscribers();
357 self.apply_update_internal(update, Some(origin.into()), gen_summary)?;
358 Ok(())
359 }
360
361 /// Applies an update (incoming from remote channel or generated using [Awareness::update] /
362 /// [Awareness::update_with_clients] methods) and modifies a state of a current instance.
363 /// Returns an [AwarenessUpdateSummary] object informing about the changes that were applied.
364 ///
365 /// If current instance has an observer channel (see: [Awareness::with_observer]), applied
366 /// changes will also be emitted as events.
367 pub fn apply_update_summary(
368 &self,
369 update: AwarenessUpdate,
370 ) -> Result<Option<AwarenessUpdateSummary>, Error> {
371 self.apply_update_internal(update, None, true)
372 }
373
374 /// Applies an update (incoming from remote channel or generated using [Awareness::update] /
375 /// [Awareness::update_with_clients] methods) and modifies a state of a current instance.
376 /// Returns an [AwarenessUpdateSummary] object informing about the changes that were applied.
377 ///
378 /// If current instance has an observer channel (see: [Awareness::with_observer]), applied
379 /// changes will also be emitted as events.
380 pub fn apply_update_summary_with<O: Into<Origin>>(
381 &self,
382 update: AwarenessUpdate,
383 origin: O,
384 ) -> Result<Option<AwarenessUpdateSummary>, Error> {
385 self.apply_update_internal(update, Some(origin.into()), true)
386 }
387
388 fn apply_update_internal(
389 &self,
390 update: AwarenessUpdate,
391 origin: Option<Origin>,
392 generate_summary: bool,
393 ) -> Result<Option<AwarenessUpdateSummary>, Error> {
394 let now = self.clock.now();
395
396 let mut added = Vec::new();
397 let mut updated = Vec::new();
398 let mut changed = Vec::new();
399 let mut removed = Vec::new();
400
401 for (client_id, entry) in update.clients {
402 let mut clock = entry.clock;
403 let new: Option<_> = if entry.json.as_ref() == NULL_STR {
404 None
405 } else {
406 Some(entry.json)
407 };
408 match self.states.entry(client_id) {
409 Entry::Occupied(mut e) => {
410 let state: &mut ClientState = e.get_mut();
411 let is_removed = state.clock == clock && new.is_none() && state.data.is_some();
412 if state.clock < clock || is_removed {
413 match new {
414 None => {
415 // never let a remote client remove this local state
416 if client_id == self.doc.client_id() && state.data.is_some() {
417 // remote client removed the local state. Do not remove state. Broadcast a message indicating
418 // that this client still exists by increasing the clock
419 clock += 1;
420 } else {
421 state.data = None;
422 if generate_summary {
423 removed.push(client_id);
424 }
425 }
426 }
427 new => {
428 if generate_summary {
429 updated.push(client_id);
430 if state.data.is_some() && state.data != new {
431 changed.push(client_id);
432 }
433 }
434 state.data = new;
435 }
436 }
437 state.clock = clock;
438 state.last_updated = now;
439 true
440 } else {
441 false
442 }
443 }
444 Entry::Vacant(e) => {
445 let has_data = new.is_some();
446 e.insert(ClientState::new(clock, now, new));
447 if has_data {
448 if generate_summary {
449 added.push(client_id);
450 }
451 true
452 } else {
453 false
454 }
455 }
456 };
457 }
458
459 if !added.is_empty() || !updated.is_empty() || !removed.is_empty() {
460 let summary = if self.on_update.has_subscribers() || self.on_change.has_subscribers() {
461 let mut e = Event::new(added, changed, removed);
462 if !e.is_empty() {
463 self.on_change.trigger(|fun| fun(self, &e, origin.as_ref()));
464 }
465 e.summary.updated = updated;
466 self.on_update.trigger(|fun| fun(self, &e, origin.as_ref()));
467 e.summary
468 } else {
469 AwarenessUpdateSummary {
470 added,
471 updated,
472 removed,
473 }
474 };
475 Ok(Some(summary))
476 } else {
477 Ok(None)
478 }
479 }
480}
481
482#[cfg(not(target_family = "wasm"))]
483impl Default for Awareness {
484 fn default() -> Self {
485 Awareness::new(Doc::new())
486 }
487}
488
489impl std::fmt::Debug for Awareness {
490 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
491 let mut s = f.debug_struct("Awareness");
492 s.field("doc", &self.doc);
493 s.field("states", &self.states);
494 s.finish()
495 }
496}
497
498pub struct AwarenessIter<'a>(dashmap::iter::Iter<'a, ClientID, ClientState>);
499
500impl<'a> Iterator for AwarenessIter<'a> {
501 type Item = (ClientID, ClientState);
502
503 fn next(&mut self) -> Option<Self::Item> {
504 let e = self.0.next()?;
505 Some((*e.key(), e.value().clone()))
506 }
507}
508
509/// Summary of applying an [AwarenessUpdate] over [Awareness::apply_update_summary] method.
510#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
511pub struct AwarenessUpdateSummary {
512 /// New clients added as part of the update.
513 pub added: Vec<ClientID>,
514 /// Existing clients that have been changed by the update.
515 pub updated: Vec<ClientID>,
516 /// Existing clients that have been removed by the update.
517 pub removed: Vec<ClientID>,
518}
519
520impl AwarenessUpdateSummary {
521 /// Returns a collection of all clients that have been changed by the [AwarenessUpdate].
522 pub fn all_changes(&self) -> Vec<ClientID> {
523 let mut res =
524 Vec::with_capacity(self.added.len() + self.updated.len() + self.removed.len());
525 res.extend_from_slice(&self.added);
526 res.extend_from_slice(&self.updated);
527 res.extend_from_slice(&self.removed);
528 res
529 }
530}
531
532/// A structure that represents an encodable state of an [Awareness] struct.
533#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
534pub struct AwarenessUpdate {
535 /// Client updates.
536 pub clients: HashMap<ClientID, AwarenessUpdateEntry>,
537}
538
539impl Encode for AwarenessUpdate {
540 fn encode<E: Encoder>(&self, encoder: &mut E) {
541 encoder.write_var(self.clients.len());
542 for (&client_id, e) in self.clients.iter() {
543 encoder.write_var(client_id);
544 encoder.write_var(e.clock);
545 encoder.write_string(&e.json);
546 }
547 }
548}
549
550impl Decode for AwarenessUpdate {
551 fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, crate::encoding::read::Error> {
552 let len: usize = decoder.read_var()?;
553 let mut clients = HashMap::with_capacity(len);
554 for _ in 0..len {
555 let client_id: ClientID = decoder.read_var()?;
556 let clock: u32 = decoder.read_var()?;
557 let json: Arc<str> = decoder.read_string()?.into();
558 clients.insert(client_id, AwarenessUpdateEntry { clock, json });
559 }
560
561 Ok(AwarenessUpdate { clients })
562 }
563}
564
565/// A single client entry of an [AwarenessUpdate]. It consists of logical clock and JSON client
566/// state represented as a string.
567#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
568pub struct AwarenessUpdateEntry {
569 /// Timestamp used to recognize which update is the latest one.
570 pub clock: u32,
571 /// String with JSON payload containing user data.
572 pub json: Arc<str>,
573}
574
575/// Errors generated by an [Awareness] struct methods.
576#[derive(Error, Debug)]
577pub enum Error {
578 /// Client ID was not found in [Awareness] metadata.
579 #[error("client ID `{0}` not found")]
580 ClientNotFound(ClientID),
581 #[error("couldn't serialize awareness state: {0}")]
582 Serde(#[from] serde_json::Error),
583}
584
585#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
586pub struct ClientState {
587 pub clock: u32,
588 pub last_updated: Timestamp,
589 pub data: Option<Arc<str>>,
590}
591
592impl ClientState {
593 fn new(clock: u32, last_updated: Timestamp, state: Option<Arc<str>>) -> Self {
594 ClientState {
595 clock,
596 last_updated,
597 data: state,
598 }
599 }
600}
601
602/// Event type emitted by an [Awareness] struct.
603#[derive(Debug, Clone, PartialEq)]
604pub struct Event {
605 summary: AwarenessUpdateSummary,
606}
607
608impl Event {
609 pub fn new(added: Vec<ClientID>, updated: Vec<ClientID>, removed: Vec<ClientID>) -> Self {
610 Event {
611 summary: AwarenessUpdateSummary {
612 added,
613 updated,
614 removed,
615 },
616 }
617 }
618
619 pub fn summary(&self) -> &AwarenessUpdateSummary {
620 &self.summary
621 }
622
623 /// Collection of new clients that have been added to an [Awareness] struct, that was not known
624 /// before. Actual client state can be accessed via `awareness.clients().get(client_id)`.
625 pub fn added(&self) -> &[ClientID] {
626 &self.summary.added
627 }
628
629 /// Collection of new clients that have been updated within an [Awareness] struct since the last
630 /// update. Actual client state can be accessed via `awareness.clients().get(client_id)`.
631 pub fn updated(&self) -> &[ClientID] {
632 &self.summary.updated
633 }
634
635 /// Collection of new clients that have been removed from [Awareness] struct since the last
636 /// update.
637 pub fn removed(&self) -> &[ClientID] {
638 &self.summary.removed
639 }
640
641 /// Returns all the changed client IDs (added, updated and removed) combined.
642 pub fn all_changes(&self) -> Vec<ClientID> {
643 self.summary.all_changes()
644 }
645
646 fn is_empty(&self) -> bool {
647 self.summary.added.is_empty()
648 && self.summary.updated.is_empty()
649 && self.summary.removed.is_empty()
650 }
651}
652
653#[cfg(test)]
654mod test {
655 use arc_swap::ArcSwapOption;
656 use serde_json::{json, Value};
657 use std::collections::HashMap;
658 use std::sync::Arc;
659
660 use crate::sync::awareness::{AwarenessUpdateSummary, Event};
661 use crate::sync::Awareness;
662 use crate::Doc;
663
664 #[test]
665 fn awareness() {
666 fn exchange(recv: &ArcSwapOption<Event>, from: &Awareness, to: &mut Awareness) {
667 let e = recv.swap(None);
668 if let Some(e) = e.as_deref() {
669 let total = [e.added(), e.updated(), e.removed()].concat();
670 let u = from.update_with_clients(total).unwrap();
671 to.apply_update(u).unwrap();
672 }
673 }
674
675 let local = Awareness::new(Doc::with_client_id(1));
676 let last_change_local = Arc::new(ArcSwapOption::default());
677 let update = Arc::new(ArcSwapOption::default());
678 let _sub_update = {
679 let update = update.clone();
680 local.on_update(move |_, e, _| update.store(Some(Arc::new(e.clone()))))
681 };
682 let _sub_local = {
683 let last_change_local = last_change_local.clone();
684 local.on_change(move |_, e, _| last_change_local.store(Some(Arc::new(e.clone()))))
685 };
686
687 let mut remote = Awareness::new(Doc::with_client_id(2));
688 let last_change_remote = Arc::new(ArcSwapOption::default());
689 let _sub_remote = {
690 let last_change_remote = last_change_remote.clone();
691 remote.on_change(move |_, e, _| last_change_remote.store(Some(Arc::new(e.clone()))))
692 };
693
694 assert!(local.on_change.has_subscribers(), "local has subscribers");
695 assert!(remote.on_change.has_subscribers(), "remote has subscribers");
696 local.set_local_state(json!({"x":3})).unwrap();
697 exchange(&update, &local, &mut remote);
698 let _e_local = last_change_local.swap(None).unwrap();
699 assert_eq!(remote.state::<Value>(1).unwrap(), json!({"x":3}));
700 assert_eq!(remote.states.get(&1).unwrap().clock, 1);
701 assert_eq!(last_change_remote.swap(None).unwrap().added(), &[1]);
702
703 local.set_local_state(json!({"x":4})).unwrap();
704 exchange(&update, &local, &mut remote);
705 let e_local = last_change_local.swap(None).unwrap();
706 let e_remote = last_change_remote.swap(None).unwrap();
707 assert_eq!(remote.state::<Value>(1).unwrap(), json!({"x":4}));
708 assert_eq!(
709 e_remote.summary,
710 AwarenessUpdateSummary {
711 added: vec![],
712 updated: vec![1],
713 removed: vec![]
714 }
715 );
716 assert_eq!(e_remote.summary, e_local.summary);
717
718 local.set_local_state(json!({"x":4})).unwrap();
719 exchange(&update, &local, &mut remote);
720 let e_local = last_change_local.swap(None);
721 let e_remote = last_change_remote.swap(None);
722 assert_eq!(remote.state::<Value>(1).unwrap(), json!({"x":4}));
723 assert_eq!(remote.states.get(&1).unwrap().clock, 3);
724 assert_eq!(e_remote, e_local);
725
726 local.clean_local_state();
727 exchange(&update, &local, &mut remote);
728 let e_local = last_change_local.swap(None).unwrap();
729 let e_remote = last_change_remote.swap(None).unwrap();
730 assert_eq!(e_remote.removed().len(), 1);
731 assert!(local.state::<Value>(1).is_none());
732 assert_eq!(e_remote.summary, e_local.summary);
733 assert_eq!(e_remote, e_local);
734 }
735
736 #[test]
737 fn awareness_summary() -> Result<(), Box<dyn std::error::Error>> {
738 let local = Awareness::new(Doc::with_client_id(1));
739 let remote = Awareness::new(Doc::with_client_id(2));
740
741 local.set_local_state(json!({"x":3})).unwrap();
742 let update = local.update_with_clients([local.client_id()])?;
743 let summary = remote.apply_update_summary(update)?;
744 assert_eq!(remote.state::<Value>(1).unwrap(), json!({"x":3}));
745 assert_eq!(remote.states.get(&1).unwrap().clock, 1);
746 assert_eq!(
747 summary,
748 Some(AwarenessUpdateSummary {
749 added: vec![1],
750 updated: vec![],
751 removed: vec![]
752 })
753 );
754
755 local.set_local_state(json!({"x":4})).unwrap();
756 let update = local.update_with_clients([local.client_id()])?;
757 let summary = remote.apply_update_summary(update)?;
758 assert_eq!(remote.state::<Value>(1).unwrap(), json!({"x":4}));
759 assert_eq!(
760 summary,
761 Some(AwarenessUpdateSummary {
762 added: vec![],
763 updated: vec![1],
764 removed: vec![]
765 })
766 );
767 let local_states: HashMap<_, _> = local.iter().collect();
768 let remote_states: HashMap<_, _> = remote.iter().collect();
769 assert_eq!(local_states, remote_states);
770
771 local.clean_local_state();
772 let update = local.update_with_clients([local.client_id()])?;
773 let summary = remote.apply_update_summary(update)?;
774 assert_eq!(
775 summary,
776 Some(AwarenessUpdateSummary {
777 added: vec![],
778 updated: vec![],
779 removed: vec![1]
780 })
781 );
782 assert!(local.state::<Value>(1).is_none());
783 let local_states: HashMap<_, _> = local.iter().collect();
784 let remote_states: HashMap<_, _> = remote.iter().collect();
785 assert_eq!(local_states, remote_states);
786 Ok(())
787 }
788}
789