y-rb 0.1.7-x86_64-darwin → 0.2.0-x86_64-darwin
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/yrb/Cargo.toml +2 -1
- data/ext/yrb/src/awareness.rs +440 -0
- data/ext/yrb/src/lib.rs +104 -0
- data/ext/yrb/src/yarray.rs +10 -10
- data/ext/yrb/src/yattrs.rs +2 -2
- data/ext/yrb/src/yawareness.rs +163 -0
- data/ext/yrb/src/ydoc.rs +4 -4
- data/ext/yrb/src/ymap.rs +3 -3
- data/ext/yrb/src/ytext.rs +3 -3
- data/ext/yrb/src/ytransaction.rs +5 -5
- data/ext/yrb/src/yxml_element.rs +1 -1
- data/ext/yrb/src/yxml_text.rs +1 -1
- data/lib/2.7/yrb.bundle +0 -0
- data/lib/3.0/yrb.bundle +0 -0
- data/lib/3.1/yrb.bundle +0 -0
- data/lib/y/awareness.rb +152 -0
- data/lib/y/version.rb +1 -1
- data/lib/y-rb.rb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 361bea056150197ae668fe40f4803f8abc438df08c3bca5300f75fdce974d136
|
4
|
+
data.tar.gz: b9b4fcd67d80279571598a1875fbb70a42c4dc8c71a8f1017683f7054b708d33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54f92363c5aca4b41be30a77ff7e440f6a1d8c0d4ded930009f2e41289aa95f8d841563c1a493d1e26a3227c689add3b6004bc9d7cea97cea537a5028273217f
|
7
|
+
data.tar.gz: 82408fbbdd16071a7268ceecb4d273e596d86e55b7b73113aca1501ef6489dfd7c83a84f0d44e8597b1aa7bf6aac24716d927c6d2e1361666c85242a56d15be0
|
data/ext/yrb/Cargo.toml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
[package]
|
2
2
|
name = "yrb"
|
3
|
-
version = "0.
|
3
|
+
version = "0.2.0"
|
4
4
|
authors = ["Hannes Moser <box@hannesmoser.at>", "Hannes Moser <hmoser@gitlab.com>"]
|
5
5
|
edition = "2021"
|
6
6
|
homepage = "https://github.com/y-crdt/yrb"
|
@@ -9,6 +9,7 @@ repository = "https://github.com/y-crdt/yrb"
|
|
9
9
|
[dependencies]
|
10
10
|
lib0 = "0.12.0" # must match yrs version
|
11
11
|
magnus = { git = "https://github.com/matsadler/magnus", rev = "cc852bfa31992d882d42509b1165eb5f67f9dc2c" } # waiting for release with full rb-sys backend
|
12
|
+
thiserror = "1.0.37"
|
12
13
|
yrs = "0.12.0"
|
13
14
|
|
14
15
|
[dev-dependencies]
|
@@ -0,0 +1,440 @@
|
|
1
|
+
use std::cell::RefCell;
|
2
|
+
use std::collections::hash_map::Entry;
|
3
|
+
use std::collections::HashMap;
|
4
|
+
use std::rc::{Rc, Weak};
|
5
|
+
use std::time::Instant;
|
6
|
+
use thiserror::Error;
|
7
|
+
use yrs::block::ClientID;
|
8
|
+
use yrs::updates::decoder::{Decode, Decoder};
|
9
|
+
use yrs::updates::encoder::{Encode, Encoder};
|
10
|
+
use yrs::{Doc, SubscriptionId};
|
11
|
+
|
12
|
+
const NULL_STR: &str = "null";
|
13
|
+
|
14
|
+
/// The Awareness class implements a simple shared state protocol that can be used for non-persistent
|
15
|
+
/// data like awareness information (cursor, username, status, ..). Each client can update its own
|
16
|
+
/// local state and listen to state changes of remote clients.
|
17
|
+
///
|
18
|
+
/// Each client is identified by a unique client id (something we borrow from `doc.clientID`).
|
19
|
+
/// A client can override its own state by propagating a message with an increasing timestamp
|
20
|
+
/// (`clock`). If such a message is received, it is applied if the known state of that client is
|
21
|
+
/// older than the new state (`clock < new_clock`). If a client thinks that a remote client is
|
22
|
+
/// offline, it may propagate a message with `{ clock, state: null, client }`. If such a message is
|
23
|
+
/// received, and the known clock of that client equals the received clock, it will clean the state.
|
24
|
+
///
|
25
|
+
/// Before a client disconnects, it should propagate a `null` state with an updated clock.
|
26
|
+
pub struct Awareness {
|
27
|
+
doc: Doc,
|
28
|
+
states: HashMap<ClientID, String>,
|
29
|
+
meta: HashMap<ClientID, MetaClientState>,
|
30
|
+
on_update: Option<EventHandler<Event>>
|
31
|
+
}
|
32
|
+
|
33
|
+
unsafe impl Send for Awareness {}
|
34
|
+
unsafe impl Sync for Awareness {}
|
35
|
+
|
36
|
+
impl Awareness {
|
37
|
+
/// Creates a new instance of [Awareness] struct, which operates over a given document.
|
38
|
+
/// Awareness instance has full ownership of that document. If necessary it can be accessed
|
39
|
+
/// using either [Awareness::doc] or [Awareness::doc_mut] methods.
|
40
|
+
pub fn new(doc: Doc) -> Self {
|
41
|
+
Awareness {
|
42
|
+
doc,
|
43
|
+
on_update: None,
|
44
|
+
states: HashMap::new(),
|
45
|
+
meta: HashMap::new()
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
/// Returns a channel receiver for an incoming awareness events. This channel can be cloned.
|
50
|
+
pub fn on_update<F>(&mut self, f: F) -> Subscription<Event>
|
51
|
+
where
|
52
|
+
F: Fn(&Awareness, &Event) -> () + 'static
|
53
|
+
{
|
54
|
+
let eh = self.on_update.get_or_insert_with(EventHandler::default);
|
55
|
+
eh.subscribe(f)
|
56
|
+
}
|
57
|
+
|
58
|
+
/// Removes a receiver for incoming awareness events.
|
59
|
+
pub fn remove_on_update(&mut self, subscription_id: u32) {
|
60
|
+
if let Some(eh) = self.on_update.as_mut() {
|
61
|
+
eh.unsubscribe(subscription_id);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
/// Returns a globally unique client ID of an underlying [Doc].
|
66
|
+
pub fn client_id(&self) -> ClientID {
|
67
|
+
self.doc.client_id
|
68
|
+
}
|
69
|
+
|
70
|
+
/// Returns a state map of all of the clients tracked by current [Awareness] instance. Those
|
71
|
+
/// states are identified by their corresponding [ClientID]s. The associated state is
|
72
|
+
/// represented and replicated to other clients as a JSON string.
|
73
|
+
pub fn clients(&self) -> &HashMap<ClientID, String> {
|
74
|
+
&self.states
|
75
|
+
}
|
76
|
+
|
77
|
+
/// Returns a JSON string state representation of a current [Awareness] instance.
|
78
|
+
pub fn local_state(&self) -> Option<&str> {
|
79
|
+
Some(self.states.get(&self.doc.client_id)?.as_str())
|
80
|
+
}
|
81
|
+
|
82
|
+
/// Sets a current [Awareness] instance state to a corresponding JSON string. This state will
|
83
|
+
/// be replicated to other clients as part of the [AwarenessUpdate] and it will trigger an event
|
84
|
+
/// to be emitted if current instance was created using [Awareness::with_observer] method.
|
85
|
+
///
|
86
|
+
pub fn set_local_state<S: Into<String>>(&mut self, json: S) {
|
87
|
+
let client_id = self.doc.client_id;
|
88
|
+
self.update_meta(client_id);
|
89
|
+
let new: String = json.into();
|
90
|
+
match self.states.entry(client_id) {
|
91
|
+
Entry::Occupied(mut e) => {
|
92
|
+
e.insert(new);
|
93
|
+
if let Some(eh) = self.on_update.as_ref() {
|
94
|
+
eh.trigger(
|
95
|
+
self,
|
96
|
+
&Event::new(vec![], vec![client_id], vec![])
|
97
|
+
);
|
98
|
+
}
|
99
|
+
}
|
100
|
+
Entry::Vacant(e) => {
|
101
|
+
e.insert(new);
|
102
|
+
if let Some(eh) = self.on_update.as_ref() {
|
103
|
+
eh.trigger(
|
104
|
+
self,
|
105
|
+
&Event::new(vec![client_id], vec![], vec![])
|
106
|
+
);
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
/// Clears out a state of a given client, effectively marking it as disconnected.
|
113
|
+
pub fn remove_state(&mut self, client_id: ClientID) {
|
114
|
+
let prev_state = self.states.remove(&client_id);
|
115
|
+
self.update_meta(client_id);
|
116
|
+
if let Some(eh) = self.on_update.as_ref() {
|
117
|
+
if prev_state.is_some() {
|
118
|
+
eh.trigger(
|
119
|
+
self,
|
120
|
+
&Event::new(
|
121
|
+
Vec::default(),
|
122
|
+
Vec::default(),
|
123
|
+
vec![client_id]
|
124
|
+
)
|
125
|
+
);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
/// Clears out a state of a current client (see: [Awareness::client_id]),
|
131
|
+
/// effectively marking it as disconnected.
|
132
|
+
pub fn clean_local_state(&mut self) {
|
133
|
+
let client_id = self.doc.client_id;
|
134
|
+
self.remove_state(client_id);
|
135
|
+
}
|
136
|
+
|
137
|
+
fn update_meta(&mut self, client_id: ClientID) {
|
138
|
+
match self.meta.entry(client_id) {
|
139
|
+
Entry::Occupied(mut e) => {
|
140
|
+
let clock = e.get().clock + 1;
|
141
|
+
let meta = MetaClientState::new(clock, Instant::now());
|
142
|
+
e.insert(meta);
|
143
|
+
}
|
144
|
+
Entry::Vacant(e) => {
|
145
|
+
e.insert(MetaClientState::new(1, Instant::now()));
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
/// Returns a serializable update object which is representation of a current Awareness state.
|
151
|
+
pub fn update(&self) -> Result<AwarenessUpdate, Error> {
|
152
|
+
let clients = self.states.keys().cloned();
|
153
|
+
self.update_with_clients(clients)
|
154
|
+
}
|
155
|
+
|
156
|
+
/// Returns a serializable update object which is representation of a current Awareness state.
|
157
|
+
/// Unlike [Awareness::update], this method variant allows to prepare update only for a subset
|
158
|
+
/// of known clients. These clients must all be known to a current [Awareness] instance,
|
159
|
+
/// otherwise a [Error::ClientNotFound] error will be returned.
|
160
|
+
pub fn update_with_clients<I: IntoIterator<Item = ClientID>>(
|
161
|
+
&self,
|
162
|
+
clients: I
|
163
|
+
) -> Result<AwarenessUpdate, Error> {
|
164
|
+
let mut res = HashMap::new();
|
165
|
+
for client_id in clients {
|
166
|
+
let clock = if let Some(meta) = self.meta.get(&client_id) {
|
167
|
+
meta.clock
|
168
|
+
} else {
|
169
|
+
return Err(Error::ClientNotFound(client_id));
|
170
|
+
};
|
171
|
+
let json = if let Some(json) = self.states.get(&client_id) {
|
172
|
+
json.clone()
|
173
|
+
} else {
|
174
|
+
String::from(NULL_STR)
|
175
|
+
};
|
176
|
+
res.insert(client_id, AwarenessUpdateEntry { clock, json });
|
177
|
+
}
|
178
|
+
Ok(AwarenessUpdate { clients: res })
|
179
|
+
}
|
180
|
+
|
181
|
+
/// Applies an update (incoming from remote channel or generated using [Awareness::update] /
|
182
|
+
/// [Awareness::update_with_clients] methods) and modifies a state of a current instance.
|
183
|
+
///
|
184
|
+
/// If current instance has an observer channel (see: [Awareness::with_observer]), applied
|
185
|
+
/// changes will also be emitted as events.
|
186
|
+
pub fn apply_update(
|
187
|
+
&mut self,
|
188
|
+
update: AwarenessUpdate
|
189
|
+
) -> Result<(), Error> {
|
190
|
+
let now = Instant::now();
|
191
|
+
|
192
|
+
let mut added = Vec::new();
|
193
|
+
let mut updated = Vec::new();
|
194
|
+
let mut removed = Vec::new();
|
195
|
+
|
196
|
+
for (client_id, entry) in update.clients {
|
197
|
+
let mut clock = entry.clock;
|
198
|
+
let is_null = entry.json.as_str() == NULL_STR;
|
199
|
+
match self.meta.entry(client_id) {
|
200
|
+
Entry::Occupied(mut e) => {
|
201
|
+
let prev = e.get();
|
202
|
+
let is_removed = prev.clock == clock
|
203
|
+
&& is_null
|
204
|
+
&& self.states.contains_key(&client_id);
|
205
|
+
let is_new = prev.clock < clock;
|
206
|
+
if is_new || is_removed {
|
207
|
+
if is_null {
|
208
|
+
// never let a remote client remove this local state
|
209
|
+
if client_id == self.doc.client_id
|
210
|
+
&& self.states.get(&client_id).is_some()
|
211
|
+
{
|
212
|
+
// remote client removed the local state. Do not remote state. Broadcast a message indicating
|
213
|
+
// that this client still exists by increasing the clock
|
214
|
+
clock += 1;
|
215
|
+
} else {
|
216
|
+
self.states.remove(&client_id);
|
217
|
+
if self.on_update.is_some() {
|
218
|
+
removed.push(client_id);
|
219
|
+
}
|
220
|
+
}
|
221
|
+
} else {
|
222
|
+
match self.states.entry(client_id) {
|
223
|
+
Entry::Occupied(mut e) => {
|
224
|
+
if self.on_update.is_some() {
|
225
|
+
updated.push(client_id);
|
226
|
+
}
|
227
|
+
e.insert(entry.json);
|
228
|
+
}
|
229
|
+
Entry::Vacant(e) => {
|
230
|
+
e.insert(entry.json);
|
231
|
+
if self.on_update.is_some() {
|
232
|
+
updated.push(client_id);
|
233
|
+
}
|
234
|
+
}
|
235
|
+
}
|
236
|
+
}
|
237
|
+
e.insert(MetaClientState::new(clock, now));
|
238
|
+
true
|
239
|
+
} else {
|
240
|
+
false
|
241
|
+
}
|
242
|
+
}
|
243
|
+
Entry::Vacant(e) => {
|
244
|
+
e.insert(MetaClientState::new(clock, now));
|
245
|
+
self.states.insert(client_id, entry.json);
|
246
|
+
if self.on_update.is_some() {
|
247
|
+
added.push(client_id);
|
248
|
+
}
|
249
|
+
true
|
250
|
+
}
|
251
|
+
};
|
252
|
+
}
|
253
|
+
|
254
|
+
if let Some(eh) = self.on_update.as_ref() {
|
255
|
+
if !added.is_empty() || !updated.is_empty() || !removed.is_empty() {
|
256
|
+
eh.trigger(self, &Event::new(added, updated, removed));
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
Ok(())
|
261
|
+
}
|
262
|
+
}
|
263
|
+
|
264
|
+
impl Default for Awareness {
|
265
|
+
fn default() -> Self {
|
266
|
+
Awareness::new(Doc::new())
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
270
|
+
struct EventHandler<T> {
|
271
|
+
seq_nr: u32,
|
272
|
+
subscribers: Rc<RefCell<HashMap<u32, Box<dyn Fn(&Awareness, &T) -> ()>>>>
|
273
|
+
}
|
274
|
+
|
275
|
+
impl<T> EventHandler<T> {
|
276
|
+
pub fn subscribe<F>(&mut self, f: F) -> Subscription<T>
|
277
|
+
where
|
278
|
+
F: Fn(&Awareness, &T) -> () + 'static
|
279
|
+
{
|
280
|
+
let subscription_id = self.seq_nr;
|
281
|
+
self.seq_nr += 1;
|
282
|
+
{
|
283
|
+
let func = Box::new(f);
|
284
|
+
let mut subs = self.subscribers.borrow_mut();
|
285
|
+
subs.insert(subscription_id, func);
|
286
|
+
}
|
287
|
+
Subscription {
|
288
|
+
subscription_id,
|
289
|
+
subscribers: Rc::downgrade(&self.subscribers)
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
pub fn unsubscribe(&mut self, subscription_id: u32) {
|
294
|
+
let mut subs = self.subscribers.borrow_mut();
|
295
|
+
subs.remove(&subscription_id);
|
296
|
+
}
|
297
|
+
|
298
|
+
pub fn trigger(&self, awareness: &Awareness, arg: &T) {
|
299
|
+
let subs = self.subscribers.borrow();
|
300
|
+
for func in subs.values() {
|
301
|
+
func(awareness, arg);
|
302
|
+
}
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
impl<T> Default for EventHandler<T> {
|
307
|
+
fn default() -> Self {
|
308
|
+
EventHandler {
|
309
|
+
seq_nr: 0,
|
310
|
+
subscribers: Rc::new(RefCell::new(HashMap::new()))
|
311
|
+
}
|
312
|
+
}
|
313
|
+
}
|
314
|
+
|
315
|
+
/// Whenever a new callback is being registered, a [Subscription] is made. Whenever this
|
316
|
+
/// subscription a registered callback is cancelled and will not be called any more.
|
317
|
+
pub struct Subscription<T> {
|
318
|
+
subscription_id: u32,
|
319
|
+
subscribers: Weak<RefCell<HashMap<u32, Box<dyn Fn(&Awareness, &T) -> ()>>>>
|
320
|
+
}
|
321
|
+
|
322
|
+
impl<T> Into<SubscriptionId> for Subscription<T> {
|
323
|
+
fn into(self) -> SubscriptionId {
|
324
|
+
let id = self.subscription_id;
|
325
|
+
std::mem::forget(self);
|
326
|
+
id
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
impl<T> Drop for Subscription<T> {
|
331
|
+
fn drop(&mut self) {
|
332
|
+
if let Some(subs) = self.subscribers.upgrade() {
|
333
|
+
let mut s = subs.borrow_mut();
|
334
|
+
s.remove(&self.subscription_id);
|
335
|
+
}
|
336
|
+
}
|
337
|
+
}
|
338
|
+
|
339
|
+
/// A structure that represents an encodable state of an [Awareness] struct.
|
340
|
+
#[derive(Debug, Eq, PartialEq)]
|
341
|
+
pub struct AwarenessUpdate {
|
342
|
+
clients: HashMap<ClientID, AwarenessUpdateEntry>
|
343
|
+
}
|
344
|
+
|
345
|
+
impl Encode for AwarenessUpdate {
|
346
|
+
fn encode<E: Encoder>(&self, encoder: &mut E) {
|
347
|
+
encoder.write_var(self.clients.len());
|
348
|
+
for (&client_id, e) in self.clients.iter() {
|
349
|
+
encoder.write_var(client_id);
|
350
|
+
encoder.write_var(e.clock);
|
351
|
+
encoder.write_string(&e.json);
|
352
|
+
}
|
353
|
+
}
|
354
|
+
}
|
355
|
+
|
356
|
+
impl Decode for AwarenessUpdate {
|
357
|
+
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, lib0::error::Error> {
|
358
|
+
let len: usize = decoder.read_var()?;
|
359
|
+
let mut clients = HashMap::with_capacity(len);
|
360
|
+
for _ in 0..len {
|
361
|
+
let client_id: ClientID = decoder.read_var()?;
|
362
|
+
let clock: u32 = decoder.read_var()?;
|
363
|
+
let json = decoder.read_string()?.to_string();
|
364
|
+
clients.insert(client_id, AwarenessUpdateEntry { clock, json });
|
365
|
+
}
|
366
|
+
|
367
|
+
Ok(AwarenessUpdate { clients })
|
368
|
+
}
|
369
|
+
}
|
370
|
+
|
371
|
+
/// A single client entry of an [AwarenessUpdate]. It consists of logical clock and JSON client
|
372
|
+
/// state represented as a string.
|
373
|
+
#[derive(Debug, Eq, PartialEq)]
|
374
|
+
pub struct AwarenessUpdateEntry {
|
375
|
+
clock: u32,
|
376
|
+
json: String
|
377
|
+
}
|
378
|
+
|
379
|
+
/// Errors generated by an [Awareness] struct methods.
|
380
|
+
#[derive(Error, Debug)]
|
381
|
+
pub enum Error {
|
382
|
+
/// Client ID was not found in [Awareness] metadata.
|
383
|
+
#[error("client ID `{0}` not found")]
|
384
|
+
ClientNotFound(ClientID)
|
385
|
+
}
|
386
|
+
|
387
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
388
|
+
struct MetaClientState {
|
389
|
+
clock: u32,
|
390
|
+
last_updated: Instant
|
391
|
+
}
|
392
|
+
|
393
|
+
impl MetaClientState {
|
394
|
+
fn new(clock: u32, last_updated: Instant) -> Self {
|
395
|
+
MetaClientState {
|
396
|
+
clock,
|
397
|
+
last_updated
|
398
|
+
}
|
399
|
+
}
|
400
|
+
}
|
401
|
+
|
402
|
+
/// Event type emitted by an [Awareness] struct.
|
403
|
+
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
404
|
+
pub struct Event {
|
405
|
+
added: Vec<ClientID>,
|
406
|
+
updated: Vec<ClientID>,
|
407
|
+
removed: Vec<ClientID>
|
408
|
+
}
|
409
|
+
|
410
|
+
impl Event {
|
411
|
+
pub fn new(
|
412
|
+
added: Vec<ClientID>,
|
413
|
+
updated: Vec<ClientID>,
|
414
|
+
removed: Vec<ClientID>
|
415
|
+
) -> Self {
|
416
|
+
Event {
|
417
|
+
added,
|
418
|
+
updated,
|
419
|
+
removed
|
420
|
+
}
|
421
|
+
}
|
422
|
+
|
423
|
+
/// Collection of new clients that have been added to an [Awareness] struct, that was not known
|
424
|
+
/// before. Actual client state can be accessed via `awareness.clients().get(client_id)`.
|
425
|
+
pub fn added(&self) -> &[ClientID] {
|
426
|
+
&self.added
|
427
|
+
}
|
428
|
+
|
429
|
+
/// Collection of new clients that have been updated within an [Awareness] struct since the last
|
430
|
+
/// update. Actual client state can be accessed via `awareness.clients().get(client_id)`.
|
431
|
+
pub fn updated(&self) -> &[ClientID] {
|
432
|
+
&self.updated
|
433
|
+
}
|
434
|
+
|
435
|
+
/// Collection of new clients that have been removed from [Awareness] struct since the last
|
436
|
+
/// update.
|
437
|
+
pub fn removed(&self) -> &[ClientID] {
|
438
|
+
&self.removed
|
439
|
+
}
|
440
|
+
}
|
data/ext/yrb/src/lib.rs
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
extern crate core;
|
2
|
+
|
1
3
|
use crate::yarray::YArray;
|
4
|
+
use crate::yawareness::{YAwareness, YAwarenessEvent, YAwarenessUpdate};
|
2
5
|
use crate::ydoc::YDoc;
|
3
6
|
use crate::ymap::YMap;
|
4
7
|
use crate::ytext::YText;
|
@@ -7,10 +10,12 @@ use crate::yxml_element::YXmlElement;
|
|
7
10
|
use crate::yxml_text::YXmlText;
|
8
11
|
use magnus::{define_module, function, method, Error, Module, Object};
|
9
12
|
|
13
|
+
mod awareness;
|
10
14
|
mod utils;
|
11
15
|
mod yany;
|
12
16
|
mod yarray;
|
13
17
|
mod yattrs;
|
18
|
+
mod yawareness;
|
14
19
|
mod ydoc;
|
15
20
|
mod ymap;
|
16
21
|
mod ytext;
|
@@ -472,5 +477,104 @@ fn init() -> Result<(), Error> {
|
|
472
477
|
)
|
473
478
|
.expect("cannot define private method: yxml_text_to_s");
|
474
479
|
|
480
|
+
let yawareness = module
|
481
|
+
.define_class("Awareness", Default::default())
|
482
|
+
.expect("cannot define class Y::Awareness");
|
483
|
+
yawareness
|
484
|
+
.define_singleton_method(
|
485
|
+
"new",
|
486
|
+
function!(YAwareness::yawareness_new, 0)
|
487
|
+
)
|
488
|
+
.expect("cannot define singleton method: yawareness_new");
|
489
|
+
yawareness
|
490
|
+
.define_private_method(
|
491
|
+
"yawareness_apply_update",
|
492
|
+
method!(YAwareness::yawareness_apply_update, 1)
|
493
|
+
)
|
494
|
+
.expect("cannot define private method: yawareness_apply_update");
|
495
|
+
yawareness
|
496
|
+
.define_private_method(
|
497
|
+
"yawareness_clean_local_state",
|
498
|
+
method!(YAwareness::yawareness_clean_local_state, 0)
|
499
|
+
)
|
500
|
+
.expect("cannot define private method: yawareness_clean_local_state");
|
501
|
+
yawareness
|
502
|
+
.define_private_method(
|
503
|
+
"yawareness_clients",
|
504
|
+
method!(YAwareness::yawareness_clients, 0)
|
505
|
+
)
|
506
|
+
.expect("cannot define private method: yawareness_clients");
|
507
|
+
yawareness
|
508
|
+
.define_private_method(
|
509
|
+
"yawareness_client_id",
|
510
|
+
method!(YAwareness::yawareness_client_id, 0)
|
511
|
+
)
|
512
|
+
.expect("cannot define private method: yawareness_client_id");
|
513
|
+
yawareness
|
514
|
+
.define_private_method(
|
515
|
+
"yawareness_local_state",
|
516
|
+
method!(YAwareness::yawareness_local_state, 0)
|
517
|
+
)
|
518
|
+
.expect("cannot define private method: yawareness_local_state");
|
519
|
+
yawareness
|
520
|
+
.define_private_method(
|
521
|
+
"yawareness_on_update",
|
522
|
+
method!(YAwareness::yawareness_on_update, 1)
|
523
|
+
)
|
524
|
+
.expect("cannot define private method: yawareness_on_update");
|
525
|
+
yawareness
|
526
|
+
.define_private_method(
|
527
|
+
"yawareness_remove_on_update",
|
528
|
+
method!(YAwareness::yawareness_remove_on_update, 1)
|
529
|
+
)
|
530
|
+
.expect("cannot define private method: yawareness_remove_on_update");
|
531
|
+
yawareness
|
532
|
+
.define_private_method(
|
533
|
+
"yawareness_remove_state",
|
534
|
+
method!(YAwareness::yawareness_remove_state, 1)
|
535
|
+
)
|
536
|
+
.expect("cannot define private method: yawareness_remove_state");
|
537
|
+
yawareness
|
538
|
+
.define_private_method(
|
539
|
+
"yawareness_set_local_state",
|
540
|
+
method!(YAwareness::yawareness_set_local_state, 1)
|
541
|
+
)
|
542
|
+
.expect("cannot define private method: yawareness_set_local_state");
|
543
|
+
yawareness
|
544
|
+
.define_private_method(
|
545
|
+
"yawareness_update",
|
546
|
+
method!(YAwareness::yawareness_update, 0)
|
547
|
+
)
|
548
|
+
.expect("cannot define private method: yawareness_update");
|
549
|
+
yawareness
|
550
|
+
.define_private_method(
|
551
|
+
"yawareness_update_with_clients",
|
552
|
+
method!(YAwareness::yawareness_update_with_clients, 1)
|
553
|
+
)
|
554
|
+
.expect("cannot define private method: yawareness_update_with_clients");
|
555
|
+
|
556
|
+
let yawareness_update = module
|
557
|
+
.define_class("AwarenessUpdate", Default::default())
|
558
|
+
.expect("cannot define class Y:AwarenessUpdate");
|
559
|
+
yawareness_update
|
560
|
+
.define_private_method(
|
561
|
+
"yawareness_update_encode",
|
562
|
+
method!(YAwarenessUpdate::yawareness_update_encode, 0)
|
563
|
+
)
|
564
|
+
.expect("cannot define private method: yawareness_update_encode");
|
565
|
+
|
566
|
+
let yawareness_event = module
|
567
|
+
.define_class("AwarenessEvent", Default::default())
|
568
|
+
.expect("cannot define class Y:AwarenessEvent");
|
569
|
+
yawareness_event
|
570
|
+
.define_method("added", method!(YAwarenessEvent::added, 0))
|
571
|
+
.expect("cannot define private method: added");
|
572
|
+
yawareness_event
|
573
|
+
.define_method("updated", method!(YAwarenessEvent::updated, 0))
|
574
|
+
.expect("cannot define private method: updated");
|
575
|
+
yawareness_event
|
576
|
+
.define_method("removed", method!(YAwarenessEvent::removed, 0))
|
577
|
+
.expect("cannot define private method: removed");
|
578
|
+
|
475
579
|
Ok(())
|
476
580
|
}
|
data/ext/yrb/src/yarray.rs
CHANGED
@@ -15,7 +15,7 @@ pub(crate) struct YArray(pub(crate) RefCell<Array>);
|
|
15
15
|
unsafe impl Send for YArray {}
|
16
16
|
|
17
17
|
impl YArray {
|
18
|
-
pub(crate) fn yarray_each(&self, block: Proc)
|
18
|
+
pub(crate) fn yarray_each(&self, block: Proc) {
|
19
19
|
self.0.borrow_mut().iter().for_each(|val| {
|
20
20
|
let yvalue = YValue::from(val);
|
21
21
|
let args = (yvalue.into(),);
|
@@ -31,7 +31,7 @@ impl YArray {
|
|
31
31
|
transaction: &YTransaction,
|
32
32
|
index: u32,
|
33
33
|
value: Value
|
34
|
-
)
|
34
|
+
) {
|
35
35
|
let yvalue = YValue::from(value);
|
36
36
|
let avalue = Any::from(yvalue);
|
37
37
|
self.0.borrow_mut().insert(
|
@@ -45,7 +45,7 @@ impl YArray {
|
|
45
45
|
transaction: &YTransaction,
|
46
46
|
index: u32,
|
47
47
|
values: RArray
|
48
|
-
)
|
48
|
+
) {
|
49
49
|
let arr: Vec<Any> = values
|
50
50
|
.each()
|
51
51
|
.into_iter()
|
@@ -103,7 +103,7 @@ impl YArray {
|
|
103
103
|
})
|
104
104
|
.partition(Result::is_ok);
|
105
105
|
|
106
|
-
if errors.
|
106
|
+
if errors.is_empty() {
|
107
107
|
let args = (RArray::from_vec(
|
108
108
|
changes.into_iter().map(Result::unwrap).collect()
|
109
109
|
),);
|
@@ -123,7 +123,7 @@ impl YArray {
|
|
123
123
|
&self,
|
124
124
|
transaction: &YTransaction,
|
125
125
|
value: Value
|
126
|
-
)
|
126
|
+
) {
|
127
127
|
let yvalue = YValue::from(value);
|
128
128
|
let avalue = Any::from(yvalue);
|
129
129
|
self.0
|
@@ -134,7 +134,7 @@ impl YArray {
|
|
134
134
|
&self,
|
135
135
|
transaction: &YTransaction,
|
136
136
|
value: Value
|
137
|
-
)
|
137
|
+
) {
|
138
138
|
let yvalue = YValue::from(value);
|
139
139
|
let avalue = Any::from(yvalue);
|
140
140
|
self.0
|
@@ -145,7 +145,7 @@ impl YArray {
|
|
145
145
|
&self,
|
146
146
|
transaction: &YTransaction,
|
147
147
|
index: u32
|
148
|
-
)
|
148
|
+
) {
|
149
149
|
self.0
|
150
150
|
.borrow_mut()
|
151
151
|
.remove(&mut transaction.0.borrow_mut(), index)
|
@@ -155,7 +155,7 @@ impl YArray {
|
|
155
155
|
transaction: &YTransaction,
|
156
156
|
index: u32,
|
157
157
|
len: u32
|
158
|
-
)
|
158
|
+
) {
|
159
159
|
self.0.borrow_mut().remove_range(
|
160
160
|
&mut transaction.0.borrow_mut(),
|
161
161
|
index,
|
@@ -170,9 +170,9 @@ impl YArray {
|
|
170
170
|
.map(|v| YValue::from(v).into())
|
171
171
|
.collect::<Vec<Value>>();
|
172
172
|
|
173
|
-
|
173
|
+
RArray::from_vec(arr)
|
174
174
|
}
|
175
|
-
pub(crate) fn yarray_unobserve(&self, subscription_id: u32)
|
175
|
+
pub(crate) fn yarray_unobserve(&self, subscription_id: u32) {
|
176
176
|
self.0.borrow_mut().unobserve(subscription_id);
|
177
177
|
}
|
178
178
|
}
|
data/ext/yrb/src/yattrs.rs
CHANGED
@@ -10,7 +10,7 @@ pub(crate) struct YAttrs(pub(crate) Attrs);
|
|
10
10
|
|
11
11
|
impl From<Attrs> for YAttrs {
|
12
12
|
fn from(value: Attrs) -> Self {
|
13
|
-
YAttrs
|
13
|
+
YAttrs(value)
|
14
14
|
}
|
15
15
|
}
|
16
16
|
|
@@ -29,7 +29,7 @@ impl From<RHash> for YAttrs {
|
|
29
29
|
})
|
30
30
|
.expect("cannot iterate attributes hash");
|
31
31
|
|
32
|
-
YAttrs
|
32
|
+
YAttrs(attrs)
|
33
33
|
}
|
34
34
|
}
|
35
35
|
|
@@ -0,0 +1,163 @@
|
|
1
|
+
use crate::awareness::{Awareness, AwarenessUpdate, Event};
|
2
|
+
use magnus::block::Proc;
|
3
|
+
use magnus::{Error, Value};
|
4
|
+
use std::borrow::Borrow;
|
5
|
+
use std::cell::RefCell;
|
6
|
+
use std::collections::HashMap;
|
7
|
+
use yrs::block::ClientID;
|
8
|
+
use yrs::updates::decoder::Decode;
|
9
|
+
use yrs::updates::encoder::Encode;
|
10
|
+
use yrs::Doc;
|
11
|
+
|
12
|
+
#[magnus::wrap(class = "Y::Awareness")]
|
13
|
+
pub(crate) struct YAwareness(pub(crate) RefCell<Awareness>);
|
14
|
+
|
15
|
+
/// SAFETY: This is safe because we only access this data when the GVL is held.
|
16
|
+
unsafe impl Send for YAwareness {}
|
17
|
+
|
18
|
+
impl YAwareness {
|
19
|
+
pub(crate) fn yawareness_new() -> Self {
|
20
|
+
let doc = Doc::new();
|
21
|
+
let awareness = Awareness::new(doc);
|
22
|
+
|
23
|
+
Self(RefCell::new(awareness))
|
24
|
+
}
|
25
|
+
|
26
|
+
pub(crate) fn yawareness_apply_update(
|
27
|
+
&self,
|
28
|
+
update: &YAwarenessUpdate
|
29
|
+
) -> Result<(), Error> {
|
30
|
+
update.decode().and_then(|value| {
|
31
|
+
self.0.borrow_mut().apply_update(value).map_err(|_error| {
|
32
|
+
Error::runtime_error("cannot decode awareness update")
|
33
|
+
})
|
34
|
+
})
|
35
|
+
}
|
36
|
+
|
37
|
+
pub(crate) fn yawareness_clean_local_state(&self) {
|
38
|
+
self.0.borrow_mut().clean_local_state();
|
39
|
+
}
|
40
|
+
|
41
|
+
pub(crate) fn yawareness_client_id(&self) -> ClientID {
|
42
|
+
self.0.borrow().client_id()
|
43
|
+
}
|
44
|
+
|
45
|
+
pub(crate) fn yawareness_clients(&self) -> HashMap<ClientID, String> {
|
46
|
+
self.0.borrow().clients().to_owned()
|
47
|
+
}
|
48
|
+
|
49
|
+
pub(crate) fn yawareness_local_state(&self) -> Option<String> {
|
50
|
+
self.0.borrow().local_state().map(|value| value.to_string())
|
51
|
+
}
|
52
|
+
|
53
|
+
pub(crate) fn yawareness_on_update(
|
54
|
+
&self,
|
55
|
+
block: Proc
|
56
|
+
) -> Result<u32, Error> {
|
57
|
+
let subscription_id = self
|
58
|
+
.0
|
59
|
+
.borrow_mut()
|
60
|
+
.on_update(move |_awareness, event| {
|
61
|
+
let awareness_event = YAwarenessEvent::from(event);
|
62
|
+
let args = (awareness_event,);
|
63
|
+
block
|
64
|
+
.call::<(YAwarenessEvent,), Value>(args)
|
65
|
+
.expect("cannot call block: on_update");
|
66
|
+
})
|
67
|
+
.into();
|
68
|
+
|
69
|
+
Ok(subscription_id)
|
70
|
+
}
|
71
|
+
|
72
|
+
pub(crate) fn yawareness_remove_on_update(&self, subscription_id: u32) {
|
73
|
+
self.0.borrow_mut().remove_on_update(subscription_id)
|
74
|
+
}
|
75
|
+
|
76
|
+
pub(crate) fn yawareness_remove_state(&self, client_id: ClientID) {
|
77
|
+
self.0.borrow_mut().remove_state(client_id)
|
78
|
+
}
|
79
|
+
|
80
|
+
pub(crate) fn yawareness_set_local_state(&self, json: String) {
|
81
|
+
self.0.borrow_mut().set_local_state(json)
|
82
|
+
}
|
83
|
+
|
84
|
+
pub(crate) fn yawareness_update(&self) -> Result<YAwarenessUpdate, Error> {
|
85
|
+
self.0
|
86
|
+
.borrow_mut()
|
87
|
+
.update()
|
88
|
+
.map(YAwarenessUpdate::from)
|
89
|
+
.map_err(|_error| Error::runtime_error("cannot update awareness"))
|
90
|
+
}
|
91
|
+
|
92
|
+
pub(crate) fn yawareness_update_with_clients(
|
93
|
+
&self,
|
94
|
+
clients: Vec<ClientID>
|
95
|
+
) -> Result<YAwarenessUpdate, Error> {
|
96
|
+
self.0
|
97
|
+
.borrow_mut()
|
98
|
+
.update_with_clients(clients)
|
99
|
+
.map(YAwarenessUpdate::from)
|
100
|
+
.map_err(|_error| {
|
101
|
+
Error::runtime_error("cannot update awareness with clients")
|
102
|
+
})
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
impl From<Awareness> for YAwareness {
|
107
|
+
fn from(value: Awareness) -> Self {
|
108
|
+
Self(RefCell::from(value))
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
#[magnus::wrap(class = "Y::AwarenessUpdate")]
|
113
|
+
pub(crate) struct YAwarenessUpdate(pub(crate) Vec<u8>);
|
114
|
+
|
115
|
+
/// SAFETY: This is safe because we only access this data when the GVL is held.
|
116
|
+
unsafe impl Send for YAwarenessUpdate {}
|
117
|
+
|
118
|
+
impl YAwarenessUpdate {
|
119
|
+
pub(crate) fn decode(&self) -> Result<AwarenessUpdate, Error> {
|
120
|
+
AwarenessUpdate::decode_v1(self.0.borrow()).map_err(|_error| {
|
121
|
+
Error::runtime_error("cannot decode awareness update")
|
122
|
+
})
|
123
|
+
}
|
124
|
+
pub(crate) fn yawareness_update_encode(&self) -> Vec<u8> {
|
125
|
+
self.0.to_vec()
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
impl From<AwarenessUpdate> for YAwarenessUpdate {
|
130
|
+
fn from(value: AwarenessUpdate) -> Self {
|
131
|
+
YAwarenessUpdate(value.encode_v1())
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
impl From<Vec<u8>> for YAwarenessUpdate {
|
136
|
+
fn from(value: Vec<u8>) -> Self {
|
137
|
+
YAwarenessUpdate(value)
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
#[magnus::wrap(class = "Y::AwarenessEvent")]
|
142
|
+
pub(crate) struct YAwarenessEvent(Event);
|
143
|
+
|
144
|
+
/// SAFETY: This is safe because we only access this data when the GVL is held.
|
145
|
+
unsafe impl Send for YAwarenessEvent {}
|
146
|
+
|
147
|
+
impl YAwarenessEvent {
|
148
|
+
pub(crate) fn added(&self) -> Vec<ClientID> {
|
149
|
+
self.0.borrow().added().to_vec()
|
150
|
+
}
|
151
|
+
pub(crate) fn updated(&self) -> Vec<ClientID> {
|
152
|
+
self.0.borrow().updated().to_vec()
|
153
|
+
}
|
154
|
+
pub(crate) fn removed(&self) -> Vec<ClientID> {
|
155
|
+
self.0.borrow().removed().to_vec()
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
impl From<&Event> for YAwarenessEvent {
|
160
|
+
fn from(value: &Event) -> Self {
|
161
|
+
Self(value.clone())
|
162
|
+
}
|
163
|
+
}
|
data/ext/yrb/src/ydoc.rs
CHANGED
@@ -26,14 +26,14 @@ impl YDoc {
|
|
26
26
|
pub(crate) fn ydoc_transact(&self) -> YTransaction {
|
27
27
|
let transaction = self.0.borrow().transact();
|
28
28
|
|
29
|
-
|
29
|
+
YTransaction(RefCell::new(transaction))
|
30
30
|
}
|
31
31
|
pub(crate) fn ydoc_encode_diff_v1(
|
32
32
|
&self,
|
33
|
-
state_vector: Vec<u8
|
33
|
+
state_vector: Vec<u8>
|
34
34
|
) -> Result<Vec<u8>, Error> {
|
35
|
-
|
35
|
+
StateVector::decode_v1(&*state_vector)
|
36
36
|
.map(|sv| self.0.borrow().encode_state_as_update_v1(&sv))
|
37
|
-
.map_err(|_e| Error::runtime_error("cannot encode diff"))
|
37
|
+
.map_err(|_e| Error::runtime_error("cannot encode diff"))
|
38
38
|
}
|
39
39
|
}
|
data/ext/yrb/src/ymap.rs
CHANGED
@@ -76,7 +76,7 @@ impl YMap {
|
|
76
76
|
EntryChange::Inserted(v) => {
|
77
77
|
let h = RHash::new();
|
78
78
|
h.aset(
|
79
|
-
Symbol::new(
|
79
|
+
Symbol::new(key),
|
80
80
|
*YValue::from(v.clone()).0.borrow()
|
81
81
|
)
|
82
82
|
.expect("cannot add change::inserted");
|
@@ -100,7 +100,7 @@ impl YMap {
|
|
100
100
|
.expect("cannot push change::updated");
|
101
101
|
|
102
102
|
let h = RHash::new();
|
103
|
-
h.aset(Symbol::new(
|
103
|
+
h.aset(Symbol::new(key), values)
|
104
104
|
.expect("cannot push change::updated");
|
105
105
|
|
106
106
|
let payload = RHash::new();
|
@@ -115,7 +115,7 @@ impl YMap {
|
|
115
115
|
EntryChange::Removed(v) => {
|
116
116
|
let h = RHash::new();
|
117
117
|
h.aset(
|
118
|
-
Symbol::new(
|
118
|
+
Symbol::new(key),
|
119
119
|
*YValue::from(v.clone()).0.borrow()
|
120
120
|
)
|
121
121
|
.expect("cannot push change::removed");
|
data/ext/yrb/src/ytext.rs
CHANGED
@@ -153,7 +153,7 @@ impl YText {
|
|
153
153
|
Delta::Retain(index, attrs) => {
|
154
154
|
let payload = RHash::new();
|
155
155
|
|
156
|
-
let yvalue = YValue::from(index
|
156
|
+
let yvalue = YValue::from(*index);
|
157
157
|
|
158
158
|
payload
|
159
159
|
.aset(delta_retain, yvalue.0.into_inner())
|
@@ -183,7 +183,7 @@ impl YText {
|
|
183
183
|
Delta::Deleted(index) => {
|
184
184
|
let payload = RHash::new();
|
185
185
|
|
186
|
-
let yvalue = YValue::from(index
|
186
|
+
let yvalue = YValue::from(*index);
|
187
187
|
|
188
188
|
payload
|
189
189
|
.aset(delta_delete, yvalue.0.into_inner())
|
@@ -194,7 +194,7 @@ impl YText {
|
|
194
194
|
})
|
195
195
|
.partition(Result::is_ok);
|
196
196
|
|
197
|
-
if errors.
|
197
|
+
if !errors.is_empty() {
|
198
198
|
// todo: make sure we respect errors and let the method fail by
|
199
199
|
// by returning a Result containing an Error
|
200
200
|
}
|
data/ext/yrb/src/ytransaction.rs
CHANGED
@@ -30,17 +30,17 @@ impl YTransaction {
|
|
30
30
|
pub(crate) fn ytransaction_get_array(&self, name: String) -> YArray {
|
31
31
|
let a = self.0.borrow_mut().get_array(&*name);
|
32
32
|
|
33
|
-
|
33
|
+
YArray(RefCell::from(a))
|
34
34
|
}
|
35
35
|
pub(crate) fn ytransaction_get_map(&self, name: String) -> YMap {
|
36
36
|
let m = self.0.borrow_mut().get_map(&*name);
|
37
37
|
|
38
|
-
|
38
|
+
YMap(RefCell::from(m))
|
39
39
|
}
|
40
40
|
pub(crate) fn ytransaction_get_text(&self, name: String) -> YText {
|
41
41
|
let t = self.0.borrow_mut().get_text(&*name);
|
42
42
|
|
43
|
-
|
43
|
+
YText(RefCell::new(t))
|
44
44
|
}
|
45
45
|
pub(crate) fn ytransaction_get_xml_element(
|
46
46
|
&self,
|
@@ -48,12 +48,12 @@ impl YTransaction {
|
|
48
48
|
) -> YXmlElement {
|
49
49
|
let el = self.0.borrow_mut().get_xml_element(&*name);
|
50
50
|
|
51
|
-
|
51
|
+
YXmlElement(RefCell::new(el))
|
52
52
|
}
|
53
53
|
pub(crate) fn ytransaction_get_xml_text(&self, name: String) -> YXmlText {
|
54
54
|
let t = self.0.borrow_mut().get_xml_text(&*name);
|
55
55
|
|
56
|
-
|
56
|
+
YXmlText(RefCell::new(t))
|
57
57
|
}
|
58
58
|
pub(crate) fn ytransaction_state_vector(&self) -> Vec<u8> {
|
59
59
|
return self.0.borrow_mut().state_vector().encode_v1();
|
data/ext/yrb/src/yxml_element.rs
CHANGED
@@ -15,7 +15,7 @@ unsafe impl Send for YXmlElement {}
|
|
15
15
|
|
16
16
|
impl YXmlElement {
|
17
17
|
pub(crate) fn yxml_element_attributes(&self) -> RHash {
|
18
|
-
RHash::from_iter(self.0.borrow().attributes()
|
18
|
+
RHash::from_iter(self.0.borrow().attributes())
|
19
19
|
}
|
20
20
|
pub(crate) fn yxml_element_first_child(&self) -> Option<Value> {
|
21
21
|
self.yxml_element_get(0)
|
data/ext/yrb/src/yxml_text.rs
CHANGED
@@ -14,7 +14,7 @@ unsafe impl Send for YXmlText {}
|
|
14
14
|
|
15
15
|
impl YXmlText {
|
16
16
|
pub(crate) fn yxml_text_attributes(&self) -> RHash {
|
17
|
-
RHash::from_iter(self.0.borrow().attributes()
|
17
|
+
RHash::from_iter(self.0.borrow().attributes())
|
18
18
|
}
|
19
19
|
pub(crate) fn yxml_text_format(
|
20
20
|
&self,
|
data/lib/2.7/yrb.bundle
CHANGED
Binary file
|
data/lib/3.0/yrb.bundle
CHANGED
Binary file
|
data/lib/3.1/yrb.bundle
CHANGED
Binary file
|
data/lib/y/awareness.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Y
|
4
|
+
class Awareness
|
5
|
+
def apply_update(update)
|
6
|
+
yawareness_apply_update(update)
|
7
|
+
end
|
8
|
+
|
9
|
+
def clean_local_state
|
10
|
+
yawareness_clean_local_state
|
11
|
+
end
|
12
|
+
|
13
|
+
def client_id
|
14
|
+
yawareness_client_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def clients
|
18
|
+
yawareness_clients
|
19
|
+
end
|
20
|
+
|
21
|
+
def local_state
|
22
|
+
yawareness_local_state
|
23
|
+
end
|
24
|
+
|
25
|
+
def local_state=(json)
|
26
|
+
yawareness_set_local_state(json)
|
27
|
+
end
|
28
|
+
|
29
|
+
def attach(callback, &block)
|
30
|
+
return yawareness_on_update(callback) unless callback.nil?
|
31
|
+
|
32
|
+
yawareness_on_update(block.to_proc) unless block.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def detach(subscription_id)
|
36
|
+
yawareness_remove_on_update(subscription_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def remove_state(client_id)
|
40
|
+
yawareness_remove_state(client_id)
|
41
|
+
end
|
42
|
+
|
43
|
+
def update
|
44
|
+
yawareness_update
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_with_clients(clients)
|
48
|
+
yawareness_update_with_clients(clients)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# @!method yawareness_apply_update(update)
|
54
|
+
# Applies an update
|
55
|
+
#
|
56
|
+
# @param [Y::AwarenessUpdate] A structure that represents an encodable state
|
57
|
+
# of an Awareness struct.
|
58
|
+
|
59
|
+
# @!method yawareness_clean_local_state
|
60
|
+
# Clears out a state of a current client , effectively marking it as
|
61
|
+
# disconnected.
|
62
|
+
|
63
|
+
# @!method yawareness_client_id
|
64
|
+
# Returns a globally unique client ID of an underlying Doc.
|
65
|
+
# @return [Integer] The Client ID
|
66
|
+
|
67
|
+
# @!method yawareness_clients
|
68
|
+
# Returns a state map of all of the clients
|
69
|
+
# tracked by current Awareness instance. Those states are identified by
|
70
|
+
# their corresponding ClientIDs. The associated state is represented and
|
71
|
+
# replicated to other clients as a JSON string.
|
72
|
+
#
|
73
|
+
# @return [Hash<Integer, String>] Map of clients
|
74
|
+
|
75
|
+
# @!method yawareness_local_state
|
76
|
+
#
|
77
|
+
# @return [String|nil] Returns a JSON string state representation of a
|
78
|
+
# current Awareness instance.
|
79
|
+
|
80
|
+
# @!method yawareness_on_update(callback, &block)
|
81
|
+
#
|
82
|
+
# @param [Proc] A callback handler for updates
|
83
|
+
# @return [Integer] The subscription ID
|
84
|
+
|
85
|
+
# @!method yawareness_remove_on_update(subscription_id)
|
86
|
+
#
|
87
|
+
# @param [Integer] subscription_id The subscription id to remove
|
88
|
+
|
89
|
+
# @!method yawareness_remove_state(client_id)
|
90
|
+
# Clears out a state of a given client, effectively marking it as
|
91
|
+
# disconnected.
|
92
|
+
#
|
93
|
+
# @param [Integer] A Client ID
|
94
|
+
# @return [String|nil] Returns a JSON string state representation of a
|
95
|
+
# current Awareness instance.
|
96
|
+
|
97
|
+
# @!method yawareness_set_local_state(state)
|
98
|
+
# Sets a current Awareness instance state to a corresponding JSON string.
|
99
|
+
# This state will be replicated to other clients as part of the
|
100
|
+
# AwarenessUpdate and it will trigger an event to be emitted if current
|
101
|
+
# instance was created using [Awareness::with_observer] method.
|
102
|
+
#
|
103
|
+
# @param [String] Returns a state map of all of the clients tracked by
|
104
|
+
# current Awareness instance. Those states are identified by their
|
105
|
+
# corresponding ClientIDs. The associated state is represented and
|
106
|
+
# replicated to other clients as a JSON string.
|
107
|
+
|
108
|
+
# @!method yawareness_update
|
109
|
+
# Returns a serializable update object which is representation of a
|
110
|
+
# current Awareness state.
|
111
|
+
#
|
112
|
+
# @return [Y::AwarenessUpdate] The update object
|
113
|
+
|
114
|
+
# @!method yawareness_update_with_clients(clients)
|
115
|
+
# Returns a serializable update object which is representation of a
|
116
|
+
# current Awareness state. Unlike [Y::Awareness#update], this method
|
117
|
+
# variant allows to prepare update only for a subset of known clients.
|
118
|
+
# These clients must all be known to a current Awareness instance,
|
119
|
+
# otherwise an error will be returned.
|
120
|
+
#
|
121
|
+
# @param [Array<Integer>]
|
122
|
+
# @return [Y::AwarenessUpdate] The update object
|
123
|
+
end
|
124
|
+
|
125
|
+
# rubocop:disable Lint/EmptyClass
|
126
|
+
class AwarenessEvent
|
127
|
+
private
|
128
|
+
|
129
|
+
# @!method added
|
130
|
+
# @return [Array<Integer>] Added clients
|
131
|
+
|
132
|
+
# @!method updated
|
133
|
+
# @return [Array<Integer>] Updated clients
|
134
|
+
|
135
|
+
# @!method removed
|
136
|
+
# @return [Array<Integer>] Removed clients
|
137
|
+
end
|
138
|
+
# rubocop:enable Lint/EmptyClass
|
139
|
+
|
140
|
+
class AwarenessUpdate
|
141
|
+
def encode
|
142
|
+
yawareness_update_encode
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
# @!method yawareness_update_encode
|
148
|
+
# Encode the awareness state for simple transport
|
149
|
+
#
|
150
|
+
# @return [Array<Integer>] Encoded update
|
151
|
+
end
|
152
|
+
end
|
data/lib/y/version.rb
CHANGED
data/lib/y-rb.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: y-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: x86_64-darwin
|
6
6
|
authors:
|
7
7
|
- Hannes Moser
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -76,11 +76,13 @@ extra_rdoc_files: []
|
|
76
76
|
files:
|
77
77
|
- ext/yrb/Cargo.toml
|
78
78
|
- ext/yrb/extconf.rb
|
79
|
+
- ext/yrb/src/awareness.rs
|
79
80
|
- ext/yrb/src/lib.rs
|
80
81
|
- ext/yrb/src/utils.rs
|
81
82
|
- ext/yrb/src/yany.rs
|
82
83
|
- ext/yrb/src/yarray.rs
|
83
84
|
- ext/yrb/src/yattrs.rs
|
85
|
+
- ext/yrb/src/yawareness.rs
|
84
86
|
- ext/yrb/src/ydoc.rs
|
85
87
|
- ext/yrb/src/ymap.rs
|
86
88
|
- ext/yrb/src/ytext.rs
|
@@ -94,6 +96,7 @@ files:
|
|
94
96
|
- lib/y-rb.rb
|
95
97
|
- lib/y.rb
|
96
98
|
- lib/y/array.rb
|
99
|
+
- lib/y/awareness.rb
|
97
100
|
- lib/y/doc.rb
|
98
101
|
- lib/y/map.rb
|
99
102
|
- lib/y/text.rb
|