| 1 | use crate::block::{BlockCell, Item, ItemContent, ItemPosition, ItemPtr, Prelim}; |
| 2 | use crate::types::array::ArrayEvent; |
| 3 | use crate::types::map::MapEvent; |
| 4 | use crate::types::text::TextEvent; |
| 5 | use crate::types::xml::{XmlEvent, XmlTextEvent}; |
| 6 | use crate::types::{ |
| 7 | Entries, Event, Events, Path, PathSegment, RootRef, SharedRef, TypePtr, TypeRef, |
| 8 | }; |
| 9 | use crate::{ |
| 10 | ArrayRef, Doc, MapRef, Observer, Origin, Out, ReadTxn, Subscription, TextRef, TransactionMut, |
| 11 | WriteTxn, XmlElementRef, XmlFragmentRef, XmlTextRef, ID, |
| 12 | }; |
| 13 | use serde::{Deserialize, Serialize}; |
| 14 | use std::borrow::Borrow; |
| 15 | use std::collections::{HashMap, HashSet, VecDeque}; |
| 16 | use std::convert::TryFrom; |
| 17 | use std::fmt::Formatter; |
| 18 | use std::hash::{Hash, Hasher}; |
| 19 | use std::marker::PhantomData; |
| 20 | use std::ops::{Deref, DerefMut}; |
| 21 | use std::ptr::NonNull; |
| 22 | use std::sync::Arc; |
| 23 | |
| 24 | /// A wrapper around [Branch] cell, supplied with a bunch of convenience methods to operate on both |
| 25 | /// map-like and array-like contents of a [Branch]. |
| 26 | #[repr(transparent)] |
| 27 | #[derive(Clone, Copy, Hash)] |
| 28 | pub struct BranchPtr(NonNull<Branch>); |
| 29 | |
| 30 | unsafe impl Send for BranchPtr {} |
| 31 | unsafe impl Sync for BranchPtr {} |
| 32 | |
| 33 | impl BranchPtr { |
| 34 | pub(crate) fn trigger( |
| 35 | &self, |
| 36 | txn: &TransactionMut, |
| 37 | subs: HashSet<Option<Arc<str>>>, |
| 38 | ) -> Option<Event> { |
| 39 | let e = self.make_event(subs)?; |
| 40 | self.observers.trigger(|fun| fun(txn, &e)); |
| 41 | Some(e) |
| 42 | } |
| 43 | |
| 44 | pub(crate) fn trigger_deep(&self, txn: &TransactionMut, e: &Events) { |
| 45 | self.deep_observers.trigger(|fun| fun(txn, e)); |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | impl TryFrom<ItemPtr> for BranchPtr { |
| 50 | type Error = ItemPtr; |
| 51 | |
| 52 | fn try_from(value: ItemPtr) -> Result<Self, Self::Error> { |
| 53 | if let ItemContent::Type(branch) = &value.content { |
| 54 | Ok(BranchPtr::from(branch)) |
| 55 | } else { |
| 56 | Err(value) |
| 57 | } |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | impl Into<TypePtr> for BranchPtr { |
| 62 | fn into(self) -> TypePtr { |
| 63 | TypePtr::Branch(self) |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | impl Into<Origin> for BranchPtr { |
| 68 | fn into(self) -> Origin { |
| 69 | let addr = self.0.as_ptr() as usize; |
| 70 | let bytes = addr.to_be_bytes(); |
| 71 | Origin::from(bytes.as_ref()) |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | impl AsRef<Branch> for BranchPtr { |
| 76 | fn as_ref(&self) -> &Branch { |
| 77 | self.deref() |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | impl AsMut<Branch> for BranchPtr { |
| 82 | fn as_mut(&mut self) -> &mut Branch { |
| 83 | self.deref_mut() |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | impl Deref for BranchPtr { |
| 88 | type Target = Branch; |
| 89 | |
| 90 | fn deref(&self) -> &Self::Target { |
| 91 | unsafe { self.0.as_ref() } |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | impl DerefMut for BranchPtr { |
| 96 | fn deref_mut(&mut self) -> &mut Self::Target { |
| 97 | unsafe { self.0.as_mut() } |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | impl<'a> From<&'a mut Box<Branch>> for BranchPtr { |
| 102 | fn from(branch: &'a mut Box<Branch>) -> Self { |
| 103 | let ptr = NonNull::from(branch.as_ref()); |
| 104 | BranchPtr(ptr) |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | impl<'a> From<&'a Box<Branch>> for BranchPtr { |
| 109 | fn from(branch: &'a Box<Branch>) -> Self { |
| 110 | let b: &Branch = &*branch; |
| 111 | |
| 112 | let ptr = unsafe { NonNull::new_unchecked(b as *const Branch as *mut Branch) }; |
| 113 | BranchPtr(ptr) |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | impl<'a> From<&'a Branch> for BranchPtr { |
| 118 | fn from(branch: &'a Branch) -> Self { |
| 119 | let ptr = unsafe { NonNull::new_unchecked(branch as *const Branch as *mut Branch) }; |
| 120 | BranchPtr(ptr) |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | impl Into<Out> for BranchPtr { |
| 125 | /// Converts current branch data into a [Out]. It uses a type ref information to resolve, |
| 126 | /// which value variant is a correct one for this branch. Since branch represent only complex |
| 127 | /// types [Out::Any] will never be returned from this method. |
| 128 | fn into(self) -> Out { |
| 129 | match self.type_ref() { |
| 130 | TypeRef::Array => Out::YArray(ArrayRef::from(self)), |
| 131 | TypeRef::Map => Out::YMap(MapRef::from(self)), |
| 132 | TypeRef::Text => Out::YText(TextRef::from(self)), |
| 133 | TypeRef::XmlElement(_) => Out::YXmlElement(XmlElementRef::from(self)), |
| 134 | TypeRef::XmlFragment => Out::YXmlFragment(XmlFragmentRef::from(self)), |
| 135 | TypeRef::XmlText => Out::YXmlText(XmlTextRef::from(self)), |
| 136 | //TYPE_REFS_XML_HOOK => Value::YXmlHook(XmlHookRef::from(self)), |
| 137 | #[cfg(feature = "weak")] |
| 138 | TypeRef::WeakLink(_) => Out::YWeakLink(crate::WeakRef::from(self)), |
| 139 | _ => Out::UndefinedRef(self), |
| 140 | } |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | impl Eq for BranchPtr {} |
| 145 | |
| 146 | #[cfg(not(test))] |
| 147 | impl PartialEq for BranchPtr { |
| 148 | fn eq(&self, other: &Self) -> bool { |
| 149 | std::ptr::eq(self.0.as_ptr(), other.0.as_ptr()) |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | #[cfg(test)] |
| 154 | impl PartialEq for BranchPtr { |
| 155 | fn eq(&self, other: &Self) -> bool { |
| 156 | if NonNull::eq(&self.0, &other.0) { |
| 157 | true |
| 158 | } else { |
| 159 | let a: &Branch = self.deref(); |
| 160 | let b: &Branch = other.deref(); |
| 161 | a.eq(b) |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | impl std::fmt::Debug for BranchPtr { |
| 167 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 168 | write!(f, "{:?}", self.id()) |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | /// Branch describes a content of a complex Yrs data structures, such as arrays or maps. |
| 173 | pub struct Branch { |
| 174 | /// A pointer to a first block of a indexed sequence component of this branch node. If `None`, |
| 175 | /// it means that sequence is empty or a branch doesn't act as an indexed sequence. Indexed |
| 176 | /// sequences include: |
| 177 | /// |
| 178 | /// - [Array]: all elements are stored as a double linked list, while the head of the list is |
| 179 | /// kept in this field. |
| 180 | /// - [XmlElement]: this field acts as a head to a first child element stored within current XML |
| 181 | /// node. |
| 182 | /// - [Text] and [XmlText]: this field point to a first chunk of text appended to collaborative |
| 183 | /// text data structure. |
| 184 | pub(crate) start: Option<ItemPtr>, |
| 185 | |
| 186 | /// A map component of this branch node, used by some of the specialized complex types |
| 187 | /// including: |
| 188 | /// |
| 189 | /// - [Map]: all of the map elements are based on this field. The value of each entry points |
| 190 | /// to the last modified value. |
| 191 | /// - [XmlElement]: this field stores attributes assigned to a given XML node. |
| 192 | pub(crate) map: HashMap<Arc<str>, ItemPtr>, |
| 193 | |
| 194 | /// Unique identifier of a current branch node. It can be contain either a named string - which |
| 195 | /// means, this branch is a root-level complex data structure - or a block identifier. In latter |
| 196 | /// case it means, that this branch is a complex type (eg. Map or Array) nested inside of |
| 197 | /// another complex type. |
| 198 | pub(crate) item: Option<ItemPtr>, |
| 199 | |
| 200 | /// For root-level types, this is a name of a branch. |
| 201 | pub(crate) name: Option<Arc<str>>, |
| 202 | |
| 203 | /// A length of an indexed sequence component of a current branch node. Map component elements |
| 204 | /// are computed on demand. |
| 205 | pub block_len: u32, |
| 206 | |
| 207 | pub content_len: u32, |
| 208 | |
| 209 | /// An identifier of an underlying complex data type (eg. is it an Array or a Map). |
| 210 | pub(crate) type_ref: TypeRef, |
| 211 | |
| 212 | pub(crate) observers: Observer<ObserveFn>, |
| 213 | |
| 214 | pub(crate) deep_observers: Observer<DeepObserveFn>, |
| 215 | } |
| 216 | |
| 217 | #[cfg(feature = "sync")] |
| 218 | type ObserveFn = Box<dyn Fn(&TransactionMut, &Event) + Send + Sync + 'static>; |
| 219 | #[cfg(feature = "sync")] |
| 220 | type DeepObserveFn = Box<dyn Fn(&TransactionMut, &Events) + Send + Sync + 'static>; |
| 221 | |
| 222 | #[cfg(not(feature = "sync"))] |
| 223 | type ObserveFn = Box<dyn Fn(&TransactionMut, &Event) + 'static>; |
| 224 | #[cfg(not(feature = "sync"))] |
| 225 | type DeepObserveFn = Box<dyn Fn(&TransactionMut, &Events) + 'static>; |
| 226 | |
| 227 | impl std::fmt::Debug for Branch { |
| 228 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 229 | std::fmt::Display::fmt(self, f) |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | impl Eq for Branch {} |
| 234 | |
| 235 | impl PartialEq for Branch { |
| 236 | fn eq(&self, other: &Self) -> bool { |
| 237 | self.item == other.item |
| 238 | && self.start == other.start |
| 239 | && self.map == other.map |
| 240 | && self.block_len == other.block_len |
| 241 | && self.type_ref == other.type_ref |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | impl Branch { |
| 246 | pub fn new(type_ref: TypeRef) -> Box<Self> { |
| 247 | Box::new(Self { |
| 248 | start: None, |
| 249 | map: HashMap::default(), |
| 250 | block_len: 0, |
| 251 | content_len: 0, |
| 252 | item: None, |
| 253 | name: None, |
| 254 | type_ref, |
| 255 | observers: Observer::default(), |
| 256 | deep_observers: Observer::default(), |
| 257 | }) |
| 258 | } |
| 259 | |
| 260 | pub fn is_deleted(&self) -> bool { |
| 261 | match self.item { |
| 262 | Some(ptr) => ptr.is_deleted(), |
| 263 | None => false, |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | pub fn id(&self) -> BranchID { |
| 268 | if let Some(ptr) = self.item { |
| 269 | BranchID::Nested(ptr.id) |
| 270 | } else if let Some(name) = &self.name { |
| 271 | BranchID::Root(name.clone()) |
| 272 | } else { |
| 273 | unreachable!("Could not get ID for branch") |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | pub fn as_subdoc(&self) -> Option<Doc> { |
| 278 | let item = self.item?; |
| 279 | if let ItemContent::Doc(_, doc) = &item.content { |
| 280 | Some(doc.clone()) |
| 281 | } else { |
| 282 | None |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | /// Returns an identifier of an underlying complex data type (eg. is it an Array or a Map). |
| 287 | pub fn type_ref(&self) -> &TypeRef { |
| 288 | &self.type_ref |
| 289 | } |
| 290 | |
| 291 | pub(crate) fn repair_type_ref(&mut self, type_ref: TypeRef) { |
| 292 | if self.type_ref == TypeRef::Undefined { |
| 293 | self.type_ref = type_ref; |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | /// Returns a length of an indexed sequence component of a current branch node. |
| 298 | /// Map component elements are computed on demand. |
| 299 | pub fn len(&self) -> u32 { |
| 300 | self.block_len |
| 301 | } |
| 302 | |
| 303 | pub fn content_len(&self) -> u32 { |
| 304 | self.content_len |
| 305 | } |
| 306 | |
| 307 | /// Get iterator over (String, Block) entries of a map component of a current root type. |
| 308 | /// Deleted blocks are skipped by this iterator. |
| 309 | pub(crate) fn entries<'a, T: ReadTxn + 'a>(&'a self, txn: &'a T) -> Entries<'a, &'a T, T> { |
| 310 | Entries::from_ref(&self.map, txn) |
| 311 | } |
| 312 | |
| 313 | /// Get iterator over Block entries of an array component of a current root type. |
| 314 | /// Deleted blocks are skipped by this iterator. |
| 315 | pub(crate) fn iter<'a, T: ReadTxn + 'a>(&'a self, txn: &'a T) -> Iter<'a, T> { |
| 316 | Iter::new(self.start.as_ref(), txn) |
| 317 | } |
| 318 | |
| 319 | /// Returns a materialized value of non-deleted entry under a given `key` of a map component |
| 320 | /// of a current root type. |
| 321 | pub(crate) fn get<T: ReadTxn>(&self, _txn: &T, key: &str) -> Option<Out> { |
| 322 | let item = self.map.get(key)?; |
| 323 | if !item.is_deleted() { |
| 324 | item.content.get_last() |
| 325 | } else { |
| 326 | None |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | /// Given an `index` parameter, returns an item content reference which contains that index |
| 331 | /// together with an offset inside of this content, which points precisely to an `index` |
| 332 | /// location within wrapping item content. |
| 333 | /// If `index` was outside of the array component boundary of current branch node, `None` will |
| 334 | /// be returned. |
| 335 | pub(crate) fn get_at(&self, mut index: u32) -> Option<(&ItemContent, usize)> { |
| 336 | let mut ptr = self.start.as_ref(); |
| 337 | while let Some(item) = ptr.map(ItemPtr::deref) { |
| 338 | let len = item.len(); |
| 339 | if !item.is_deleted() && item.is_countable() { |
| 340 | if index < len { |
| 341 | return Some((&item.content, index as usize)); |
| 342 | } |
| 343 | |
| 344 | index -= len; |
| 345 | } |
| 346 | ptr = item.right.as_ref(); |
| 347 | } |
| 348 | |
| 349 | None |
| 350 | } |
| 351 | |
| 352 | /// Removes an entry under given `key` of a map component of a current root type, returning |
| 353 | /// a materialized representation of value stored underneath if entry existed prior deletion. |
| 354 | pub(crate) fn remove(&self, txn: &mut TransactionMut, key: &str) -> Option<Out> { |
| 355 | let item = *self.map.get(key)?; |
| 356 | let prev = if !item.is_deleted() { |
| 357 | item.content.get_last() |
| 358 | } else { |
| 359 | None |
| 360 | }; |
| 361 | txn.delete(item); |
| 362 | prev |
| 363 | } |
| 364 | |
| 365 | /// Returns a first non-deleted item from an array component of a current root type. |
| 366 | pub(crate) fn first(&self) -> Option<&Item> { |
| 367 | let mut ptr = self.start.as_ref(); |
| 368 | while let Some(item) = ptr.map(ItemPtr::deref) { |
| 369 | if item.is_deleted() { |
| 370 | ptr = item.right.as_ref(); |
| 371 | } else { |
| 372 | return Some(item); |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | None |
| 377 | } |
| 378 | |
| 379 | /// Given an `index` and start block `ptr`, returns a pair of block pointers. |
| 380 | /// |
| 381 | /// If `index` happens to point inside of an existing block content, such block will be split at |
| 382 | /// position of an `index`. In such case left tuple value contains end of a block pointer on |
| 383 | /// a left side of an `index` and a pointer to a block directly on the right side of an `index`. |
| 384 | /// |
| 385 | /// If `index` point to the end of a block and no splitting is necessary, tuple will return only |
| 386 | /// left side (beginning of a block), while right side will be `None`. |
| 387 | /// |
| 388 | /// If `index` is outside the range of an array component of current branch node, both tuple |
| 389 | /// values will be `None`. |
| 390 | fn index_to_ptr( |
| 391 | txn: &mut TransactionMut, |
| 392 | mut ptr: Option<ItemPtr>, |
| 393 | mut index: u32, |
| 394 | ) -> (Option<ItemPtr>, Option<ItemPtr>) { |
| 395 | let encoding = txn.store.offset_kind; |
| 396 | while let Some(item) = ptr { |
| 397 | let content_len = item.content_len(encoding); |
| 398 | if !item.is_deleted() && item.is_countable() { |
| 399 | if index == content_len { |
| 400 | let left = ptr; |
| 401 | let right = item.right.clone(); |
| 402 | return (left, right); |
| 403 | } else if index < content_len { |
| 404 | let index = if let ItemContent::String(s) = &item.content { |
| 405 | s.block_offset(index, encoding) |
| 406 | } else { |
| 407 | index |
| 408 | }; |
| 409 | let right = txn.store.blocks.split_block(item, index, encoding); |
| 410 | if let Some(_) = item.moved { |
| 411 | if let Some(src) = right { |
| 412 | if let Some(&prev_dst) = txn.prev_moved.get(&item) { |
| 413 | txn.prev_moved.insert(src, prev_dst); |
| 414 | } |
| 415 | } |
| 416 | } |
| 417 | return (ptr, right); |
| 418 | } |
| 419 | index -= content_len; |
| 420 | } |
| 421 | ptr = item.right.clone(); |
| 422 | } |
| 423 | (None, None) |
| 424 | } |
| 425 | /// Removes up to a `len` of countable elements from current branch sequence, starting at the |
| 426 | /// given `index`. Returns number of removed elements. |
| 427 | pub(crate) fn remove_at(&self, txn: &mut TransactionMut, index: u32, len: u32) -> u32 { |
| 428 | let mut remaining = len; |
| 429 | let start = { self.start }; |
| 430 | let (_, mut ptr) = if index == 0 { |
| 431 | (None, start) |
| 432 | } else { |
| 433 | Branch::index_to_ptr(txn, start, index) |
| 434 | }; |
| 435 | while remaining > 0 { |
| 436 | if let Some(item) = ptr { |
| 437 | let encoding = txn.store().offset_kind; |
| 438 | if !item.is_deleted() { |
| 439 | let content_len = item.content_len(encoding); |
| 440 | let (l, r) = if remaining < content_len { |
| 441 | let offset = if let ItemContent::String(s) = &item.content { |
| 442 | s.block_offset(remaining, encoding) |
| 443 | } else { |
| 444 | remaining |
| 445 | }; |
| 446 | remaining = 0; |
| 447 | let new_right = txn.store.blocks.split_block(item, offset, encoding); |
| 448 | if let Some(_) = item.moved { |
| 449 | if let Some(src) = new_right { |
| 450 | if let Some(&prev_dst) = txn.prev_moved.get(&item) { |
| 451 | txn.prev_moved.insert(src, prev_dst); |
| 452 | } |
| 453 | } |
| 454 | } |
| 455 | (item, new_right) |
| 456 | } else { |
| 457 | remaining -= content_len; |
| 458 | (item, item.right.clone()) |
| 459 | }; |
| 460 | txn.delete(l); |
| 461 | ptr = r; |
| 462 | } else { |
| 463 | ptr = item.right.clone(); |
| 464 | } |
| 465 | } else { |
| 466 | break; |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | len - remaining |
| 471 | } |
| 472 | |
| 473 | /// Inserts a preliminary `value` into a current branch indexed sequence component at the given |
| 474 | /// `index`. Returns an item reference created as a result of this operation. |
| 475 | pub(crate) fn insert_at<V: Prelim>( |
| 476 | &self, |
| 477 | txn: &mut TransactionMut, |
| 478 | index: u32, |
| 479 | value: V, |
| 480 | ) -> Option<ItemPtr> { |
| 481 | let (start, parent) = { |
| 482 | if index <= self.len() { |
| 483 | (self.start, BranchPtr::from(self)) |
| 484 | } else { |
| 485 | panic!("Cannot insert item at index over the length of an array") |
| 486 | } |
| 487 | }; |
| 488 | let (left, right) = if index == 0 { |
| 489 | (None, None) |
| 490 | } else { |
| 491 | Branch::index_to_ptr(txn, start, index) |
| 492 | }; |
| 493 | let pos = ItemPosition { |
| 494 | parent: parent.into(), |
| 495 | left, |
| 496 | right, |
| 497 | index: 0, |
| 498 | current_attrs: None, |
| 499 | }; |
| 500 | |
| 501 | txn.create_item(&pos, value, None) |
| 502 | } |
| 503 | |
| 504 | pub(crate) fn path(from: BranchPtr, to: BranchPtr) -> Path { |
| 505 | let parent = from; |
| 506 | let mut child = to; |
| 507 | let mut path = VecDeque::default(); |
| 508 | while let Some(item) = &child.item { |
| 509 | if parent.item == child.item { |
| 510 | break; |
| 511 | } |
| 512 | let item_id = item.id.clone(); |
| 513 | let parent_sub = item.parent_sub.clone(); |
| 514 | child = *item.parent.as_branch().unwrap(); |
| 515 | if let Some(parent_sub) = parent_sub { |
| 516 | // parent is map-ish |
| 517 | path.push_front(PathSegment::Key(parent_sub)); |
| 518 | } else { |
| 519 | // parent is array-ish |
| 520 | let mut i = 0; |
| 521 | let mut c = child.start; |
| 522 | while let Some(ptr) = c { |
| 523 | if ptr.id() == &item_id { |
| 524 | break; |
| 525 | } |
| 526 | if !ptr.is_deleted() && ptr.is_countable() { |
| 527 | i += ptr.len(); |
| 528 | } |
| 529 | c = ptr.right; |
| 530 | } |
| 531 | path.push_front(PathSegment::Index(i)); |
| 532 | } |
| 533 | } |
| 534 | path |
| 535 | } |
| 536 | |
| 537 | #[cfg(feature = "sync")] |
| 538 | pub fn observe<F>(&mut self, f: F) -> Subscription |
| 539 | where |
| 540 | F: Fn(&TransactionMut, &Event) + Send + Sync + 'static, |
| 541 | { |
| 542 | self.observers.subscribe(Box::new(f)) |
| 543 | } |
| 544 | |
| 545 | #[cfg(not(feature = "sync"))] |
| 546 | pub fn observe<F>(&mut self, f: F) -> Subscription |
| 547 | where |
| 548 | F: Fn(&TransactionMut, &Event) + 'static, |
| 549 | { |
| 550 | self.observers.subscribe(Box::new(f)) |
| 551 | } |
| 552 | |
| 553 | #[cfg(feature = "sync")] |
| 554 | |
| 555 | pub fn observe_with<F>(&mut self, key: Origin, f: F) |
| 556 | where |
| 557 | F: Fn(&TransactionMut, &Event) + Send + Sync + 'static, |
| 558 | { |
| 559 | self.observers.subscribe_with(key, Box::new(f)) |
| 560 | } |
| 561 | |
| 562 | #[cfg(not(feature = "sync"))] |
| 563 | pub fn observe_with<F>(&mut self, key: Origin, f: F) |
| 564 | where |
| 565 | F: Fn(&TransactionMut, &Event) + 'static, |
| 566 | { |
| 567 | self.observers.subscribe_with(key, Box::new(f)) |
| 568 | } |
| 569 | |
| 570 | pub fn unobserve(&mut self, key: &Origin) -> bool { |
| 571 | self.observers.unsubscribe(&key) |
| 572 | } |
| 573 | |
| 574 | #[cfg(feature = "sync")] |
| 575 | pub fn observe_deep<F>(&self, f: F) -> Subscription |
| 576 | where |
| 577 | F: Fn(&TransactionMut, &Events) + Send + Sync + 'static, |
| 578 | { |
| 579 | self.deep_observers.subscribe(Box::new(f)) |
| 580 | } |
| 581 | |
| 582 | #[cfg(not(feature = "sync"))] |
| 583 | pub fn observe_deep<F>(&self, f: F) -> Subscription |
| 584 | where |
| 585 | F: Fn(&TransactionMut, &Events) + 'static, |
| 586 | { |
| 587 | self.deep_observers.subscribe(Box::new(f)) |
| 588 | } |
| 589 | |
| 590 | #[cfg(feature = "sync")] |
| 591 | pub fn observe_deep_with<F>(&self, key: Origin, f: F) |
| 592 | where |
| 593 | F: Fn(&TransactionMut, &Events) + Send + Sync + 'static, |
| 594 | { |
| 595 | self.deep_observers.subscribe_with(key, Box::new(f)) |
| 596 | } |
| 597 | |
| 598 | #[cfg(not(feature = "sync"))] |
| 599 | pub fn observe_deep_with<F>(&self, key: Origin, f: F) |
| 600 | where |
| 601 | F: Fn(&TransactionMut, &Events) + 'static, |
| 602 | { |
| 603 | self.deep_observers.subscribe_with(key, Box::new(f)) |
| 604 | } |
| 605 | |
| 606 | pub(crate) fn is_parent_of(&self, mut ptr: Option<ItemPtr>) -> bool { |
| 607 | while let Some(i) = ptr.as_deref() { |
| 608 | if let Some(parent) = i.parent.as_branch() { |
| 609 | if parent.deref() == self { |
| 610 | return true; |
| 611 | } |
| 612 | ptr = parent.item; |
| 613 | } else { |
| 614 | break; |
| 615 | } |
| 616 | } |
| 617 | false |
| 618 | } |
| 619 | |
| 620 | pub(crate) fn make_event(&self, keys: HashSet<Option<Arc<str>>>) -> Option<Event> { |
| 621 | let self_ptr = BranchPtr::from(self); |
| 622 | let event = match self.type_ref() { |
| 623 | TypeRef::Array => Event::Array(ArrayEvent::new(self_ptr)), |
| 624 | TypeRef::Map => Event::Map(MapEvent::new(self_ptr, keys)), |
| 625 | TypeRef::Text => Event::Text(TextEvent::new(self_ptr)), |
| 626 | TypeRef::XmlElement(_) | TypeRef::XmlFragment => { |
| 627 | Event::XmlFragment(XmlEvent::new(self_ptr, keys)) |
| 628 | } |
| 629 | TypeRef::XmlText => Event::XmlText(XmlTextEvent::new(self_ptr, keys)), |
| 630 | #[cfg(feature = "weak")] |
| 631 | TypeRef::WeakLink(_) => Event::Weak(crate::types::weak::WeakEvent::new(self_ptr)), |
| 632 | _ => return None, |
| 633 | }; |
| 634 | |
| 635 | Some(event) |
| 636 | } |
| 637 | } |
| 638 | |
| 639 | pub(crate) struct Iter<'a, T> { |
| 640 | ptr: Option<&'a ItemPtr>, |
| 641 | _txn: &'a T, |
| 642 | } |
| 643 | |
| 644 | impl<'a, T: ReadTxn> Iter<'a, T> { |
| 645 | fn new(ptr: Option<&'a ItemPtr>, txn: &'a T) -> Self { |
| 646 | Iter { ptr, _txn: txn } |
| 647 | } |
| 648 | } |
| 649 | |
| 650 | impl<'a, T: ReadTxn> Iterator for Iter<'a, T> { |
| 651 | type Item = &'a Item; |
| 652 | |
| 653 | fn next(&mut self) -> Option<Self::Item> { |
| 654 | let item = self.ptr.take()?; |
| 655 | self.ptr = item.right.as_ref(); |
| 656 | Some(item) |
| 657 | } |
| 658 | } |
| 659 | |
| 660 | /// A logical reference to a root-level shared collection. It can be shared across different |
| 661 | /// documents to reference the same logical type. |
| 662 | /// |
| 663 | /// # Example |
| 664 | /// |
| 665 | /// ```rust |
| 666 | /// use yrs::{Doc, RootRef, SharedRef, TextRef, Transact}; |
| 667 | /// |
| 668 | /// let root = TextRef::root("hello"); |
| 669 | /// |
| 670 | /// let doc1 = Doc::new(); |
| 671 | /// let txt1 = root.get_or_create(&mut doc1.transact_mut()); |
| 672 | /// |
| 673 | /// let doc2 = Doc::new(); |
| 674 | /// let txt2 = root.get_or_create(&mut doc2.transact_mut()); |
| 675 | /// |
| 676 | /// // instances of TextRef point to different heap objects |
| 677 | /// assert_ne!(&txt1 as *const _, &txt2 as *const _); |
| 678 | /// |
| 679 | /// // logical descriptors of both TextRef are the same as they refer to the |
| 680 | /// // same logical entity |
| 681 | /// assert_eq!(txt1.hook(), txt2.hook()); |
| 682 | /// ``` |
| 683 | #[repr(transparent)] |
| 684 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
| 685 | pub struct Root<S> { |
| 686 | /// Unique identifier of root-level shared collection. |
| 687 | pub name: Arc<str>, |
| 688 | _tag: PhantomData<S>, |
| 689 | } |
| 690 | |
| 691 | impl<S: RootRef> Root<S> { |
| 692 | /// Creates a new logical reference for a root-level shared collection of a given name and type. |
| 693 | /// Returned value can be used to resolve instances of root-level types by calling [Root::get] |
| 694 | /// or [Root::get_or_create]. |
| 695 | pub fn new<N: Into<Arc<str>>>(name: N) -> Self { |
| 696 | Root { |
| 697 | name: name.into(), |
| 698 | _tag: PhantomData::default(), |
| 699 | } |
| 700 | } |
| 701 | |
| 702 | /// Returns a reference to a shared root-level collection current [Root] represents, or creates |
| 703 | /// it if it wasn't instantiated before. |
| 704 | pub fn get_or_create<T: WriteTxn>(&self, txn: &mut T) -> S { |
| 705 | let store = txn.store_mut(); |
| 706 | let branch = store.get_or_create_type(self.name.clone(), S::type_ref()); |
| 707 | S::from(branch) |
| 708 | } |
| 709 | } |
| 710 | |
| 711 | impl<S: SharedRef> Root<S> { |
| 712 | /// Returns a reference to a shared collection current [Root] represents, or returns `None` if |
| 713 | /// that collection hasn't been instantiated yet. |
| 714 | pub fn get<T: ReadTxn>(&self, txn: &T) -> Option<S> { |
| 715 | txn.store().get_type(self.name.clone()).map(S::from) |
| 716 | } |
| 717 | } |
| 718 | |
| 719 | impl<S> Into<BranchID> for Root<S> { |
| 720 | fn into(self) -> BranchID { |
| 721 | BranchID::Root(self.name) |
| 722 | } |
| 723 | } |
| 724 | |
| 725 | /// A logical reference used to represent a shared collection nested within another one. Unlike |
| 726 | /// [Root]-level types which cannot be deleted and exist eternally, [Nested] collections can be |
| 727 | /// added (therefore don't exist prior their instantiation) and deleted (so that any [SharedRef] |
| 728 | /// values referencing them become unsafe and can point to objects that no longer exists!). |
| 729 | /// |
| 730 | /// Use [Nested::get] in order to materialize current nested logical reference into shared ref type. |
| 731 | /// |
| 732 | /// # Example |
| 733 | /// |
| 734 | /// ```rust |
| 735 | /// use yrs::{Doc, Map, Nested, SharedRef, TextPrelim, TextRef, Transact, WriteTxn}; |
| 736 | /// |
| 737 | /// let doc = Doc::new(); |
| 738 | /// let mut txn = doc.transact_mut(); |
| 739 | /// let root = txn.get_or_insert_map("root"); // root-level collection |
| 740 | /// let text = root.insert(&mut txn, "nested", TextPrelim::new("")); // nested collection |
| 741 | /// |
| 742 | /// // convert nested TextRef into logical pointer |
| 743 | /// let nested: Nested<TextRef> = text.hook().into_nested().unwrap(); |
| 744 | /// |
| 745 | /// // logical reference can be used to retrieve accessible TextRef when its alive |
| 746 | /// assert_eq!(nested.get(&txn), Some(text)); |
| 747 | /// |
| 748 | /// // delete nested collection |
| 749 | /// root.remove(&mut txn, "nested"); |
| 750 | /// |
| 751 | /// // logical reference cannot resolve shared collections that have been deleted already |
| 752 | /// assert_eq!(nested.get(&txn), None); |
| 753 | /// ``` |
| 754 | #[repr(transparent)] |
| 755 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
| 756 | pub struct Nested<S> { |
| 757 | pub id: ID, |
| 758 | _tag: PhantomData<S>, |
| 759 | } |
| 760 | |
| 761 | impl<S> Nested<S> { |
| 762 | pub fn new(id: ID) -> Self { |
| 763 | Nested { |
| 764 | id, |
| 765 | _tag: PhantomData::default(), |
| 766 | } |
| 767 | } |
| 768 | } |
| 769 | |
| 770 | impl<S: SharedRef> Nested<S> { |
| 771 | /// If current [Nested] logical reference points to an instantiated and not-deleted shared |
| 772 | /// collection, a reference to that collection will be returned. |
| 773 | /// If the referenced collection has been deleted or was not yet present in current transaction |
| 774 | /// scope i.e. due to missing update, a `None` will be returned. |
| 775 | pub fn get<T: ReadTxn>(&self, txn: &T) -> Option<S> { |
| 776 | let store = txn.store(); |
| 777 | let block = store.blocks.get_block(&self.id)?; |
| 778 | if let BlockCell::Block(block) = block { |
| 779 | if let ItemContent::Type(branch) = &block.content { |
| 780 | if let Some(ptr) = branch.item { |
| 781 | if !ptr.is_deleted() { |
| 782 | return Some(S::from(BranchPtr::from(&*branch))); |
| 783 | } |
| 784 | } |
| 785 | } |
| 786 | } |
| 787 | None |
| 788 | } |
| 789 | } |
| 790 | |
| 791 | impl<S> Into<BranchID> for Nested<S> { |
| 792 | fn into(self) -> BranchID { |
| 793 | BranchID::Nested(self.id) |
| 794 | } |
| 795 | } |
| 796 | |
| 797 | /// A descriptor used to reference to shared collections by their unique logical identifiers, |
| 798 | /// which can be either [Root]-level collections or shared collections [Nested] into each other. |
| 799 | /// It can be resolved from any shared reference using [SharedRef::hook]. |
| 800 | #[derive(Clone, Serialize, Deserialize)] |
| 801 | pub struct Hook<S> { |
| 802 | id: BranchID, |
| 803 | _tag: PhantomData<S>, |
| 804 | } |
| 805 | |
| 806 | impl<S> Hook<S> { |
| 807 | /// Unique logical identifier of a shared collection. |
| 808 | #[inline] |
| 809 | pub fn id(&self) -> &BranchID { |
| 810 | &self.id |
| 811 | } |
| 812 | } |
| 813 | |
| 814 | impl<S> std::fmt::Debug for Hook<S> { |
| 815 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
| 816 | write!(f, "{:?}", self.id) |
| 817 | } |
| 818 | } |
| 819 | |
| 820 | impl<S> Eq for Hook<S> {} |
| 821 | |
| 822 | impl<S> PartialEq for Hook<S> { |
| 823 | fn eq(&self, other: &Self) -> bool { |
| 824 | self.id == other.id |
| 825 | } |
| 826 | } |
| 827 | |
| 828 | impl<S> Hash for Hook<S> { |
| 829 | fn hash<H: Hasher>(&self, state: &mut H) { |
| 830 | self.id.hash(state) |
| 831 | } |
| 832 | } |
| 833 | |
| 834 | impl<S: SharedRef> Hook<S> { |
| 835 | /// Returns a reference to a shared collection current hook points to, if it exists and |
| 836 | /// (in case of nested collections) has not been deleted. |
| 837 | /// |
| 838 | /// # Example |
| 839 | /// |
| 840 | /// ```rust |
| 841 | /// use yrs::{Hook, Doc, Map, MapRef, Nested, SharedRef, TextPrelim, TextRef, Transact, WriteTxn}; |
| 842 | /// |
| 843 | /// let doc = Doc::new(); |
| 844 | /// let mut txn = doc.transact_mut(); |
| 845 | /// let root = txn.get_or_insert_map("root"); // root-level collection |
| 846 | /// let nested = root.insert(&mut txn, "nested", TextPrelim::new("")); // nested collection |
| 847 | /// |
| 848 | /// let root_hook: Hook<MapRef> = root.hook(); |
| 849 | /// let nested_hook: Hook<TextRef> = nested.hook(); |
| 850 | /// |
| 851 | /// // hook can be used to retrieve collection reference as long as its alive |
| 852 | /// assert_eq!(nested_hook.get(&txn), Some(nested)); |
| 853 | /// |
| 854 | /// // after nested collection is deleted it can no longer be referenced |
| 855 | /// root.remove(&mut txn, "nested"); |
| 856 | /// assert_eq!(nested_hook.get(&txn), None, "wtf"); |
| 857 | /// |
| 858 | /// // descriptors work also for root types |
| 859 | /// assert_eq!(root_hook.get(&txn), Some(root)); |
| 860 | /// ``` |
| 861 | pub fn get<T: ReadTxn>(&self, txn: &T) -> Option<S> { |
| 862 | let branch = self.id.get_branch(txn)?; |
| 863 | match branch.item { |
| 864 | Some(ptr) if ptr.is_deleted() => None, |
| 865 | _ => Some(S::from(branch)), |
| 866 | } |
| 867 | } |
| 868 | |
| 869 | /// Attempts to convert current [Hook] type into [Nested] one. |
| 870 | /// Returns `None` if current descriptor doesn't reference a nested shared collection. |
| 871 | pub fn into_nested(self) -> Option<Nested<S>> { |
| 872 | match self.id { |
| 873 | BranchID::Nested(id) => Some(Nested::new(id)), |
| 874 | BranchID::Root(_) => None, |
| 875 | } |
| 876 | } |
| 877 | } |
| 878 | |
| 879 | impl<S: RootRef> Hook<S> { |
| 880 | /// Attempts to convert current [Hook] type into [Root] one. |
| 881 | /// Returns `None` if current descriptor doesn't reference a root-level shared collection. |
| 882 | pub fn into_root(self) -> Option<Root<S>> { |
| 883 | match self.id { |
| 884 | BranchID::Root(name) => Some(Root::new(name)), |
| 885 | BranchID::Nested(_) => None, |
| 886 | } |
| 887 | } |
| 888 | } |
| 889 | |
| 890 | impl<S> From<Root<S>> for Hook<S> { |
| 891 | fn from(root: Root<S>) -> Self { |
| 892 | Hook { |
| 893 | id: root.into(), |
| 894 | _tag: PhantomData::default(), |
| 895 | } |
| 896 | } |
| 897 | } |
| 898 | |
| 899 | impl<S> From<Nested<S>> for Hook<S> { |
| 900 | fn from(nested: Nested<S>) -> Self { |
| 901 | Hook { |
| 902 | id: nested.into(), |
| 903 | _tag: PhantomData::default(), |
| 904 | } |
| 905 | } |
| 906 | } |
| 907 | |
| 908 | impl<S> From<BranchID> for Hook<S> { |
| 909 | fn from(id: BranchID) -> Self { |
| 910 | Hook { |
| 911 | id, |
| 912 | _tag: PhantomData::default(), |
| 913 | } |
| 914 | } |
| 915 | } |
| 916 | |
| 917 | impl<S> Into<BranchID> for Hook<S> { |
| 918 | fn into(self) -> BranchID { |
| 919 | self.id |
| 920 | } |
| 921 | } |
| 922 | |
| 923 | /// An unique logical identifier of a shared collection. Can be shared across document boundaries |
| 924 | /// to reference to the same logical entity across different replicas of a document. |
| 925 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)] |
| 926 | pub enum BranchID { |
| 927 | Nested(ID), |
| 928 | Root(Arc<str>), |
| 929 | } |
| 930 | |
| 931 | impl BranchID { |
| 932 | #[inline] |
| 933 | pub fn get_root<T: ReadTxn, K: Borrow<str>>(txn: &T, name: K) -> Option<BranchPtr> { |
| 934 | txn.store().get_type(name) |
| 935 | } |
| 936 | |
| 937 | pub fn get_nested<T: ReadTxn>(txn: &T, id: &ID) -> Option<BranchPtr> { |
| 938 | let block = txn.store().blocks.get_block(id)?; |
| 939 | if let BlockCell::Block(block) = block { |
| 940 | if let ItemContent::Type(branch) = &block.content { |
| 941 | return Some(BranchPtr::from(&*branch)); |
| 942 | } |
| 943 | } |
| 944 | None |
| 945 | } |
| 946 | |
| 947 | pub fn get_branch<T: ReadTxn>(&self, txn: &T) -> Option<BranchPtr> { |
| 948 | match self { |
| 949 | BranchID::Root(name) => Self::get_root(txn, name.as_ref()), |
| 950 | BranchID::Nested(id) => Self::get_nested(txn, id), |
| 951 | } |
| 952 | } |
| 953 | } |
| 954 | |
| 955 | impl std::fmt::Debug for BranchID { |
| 956 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
| 957 | match self { |
| 958 | BranchID::Nested(id) => write!(f, "{}", id), |
| 959 | BranchID::Root(name) => write!(f, "'{}'", name), |
| 960 | } |
| 961 | } |
| 962 | } |
| 963 | |