oficina / docs Public

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

Plain git — no account needed to clone.

Download

Download this file

1use std::borrow::Borrow;
2use std::cell::UnsafeCell;
3use std::collections::{HashMap, HashSet};
4use std::convert::{TryFrom, TryInto};
5use std::fmt::Write;
6use std::marker::PhantomData;
7use std::ops::{Deref, DerefMut};
8use std::sync::Arc;
9
10use crate::block::{EmbedPrelim, Item, ItemContent, ItemPosition, ItemPtr, Prelim};
11use crate::block_iter::BlockIter;
12use crate::transaction::TransactionMut;
13use crate::types::text::{diff_between, TextEvent, YChange};
14use crate::types::{
15 event_change_set, event_keys, AsPrelim, Branch, BranchPtr, Change, ChangeSet, DefaultPrelim,
16 Delta, Entries, EntryChange, MapRef, Out, Path, RootRef, SharedRef, ToJson, TypePtr, TypeRef,
17};
18use crate::{
19 Any, ArrayRef, BranchID, DeepObservable, GetString, In, IndexedSequence, Map, Observable,
20 ReadTxn, StickyIndex, Text, TextRef, ID,
21};
22
23pub trait XmlPrelim: Prelim {}
24
25/// Trait shared by preliminary types that can be used as XML nodes: [XmlElementPrelim],
26/// [XmlFragmentPrelim] and [XmlTextPrelim].
27#[derive(Debug, Clone, PartialEq)]
28pub enum XmlIn {
29 Text(XmlDeltaPrelim),
30 Element(XmlElementPrelim),
31 Fragment(XmlFragmentPrelim),
32}
33
34impl From<XmlDeltaPrelim> for XmlIn {
35 #[inline]
36 fn from(value: XmlDeltaPrelim) -> Self {
37 Self::Text(value)
38 }
39}
40
41impl From<XmlTextPrelim> for XmlIn {
42 #[inline]
43 fn from(value: XmlTextPrelim) -> Self {
44 Self::Text(value.into())
45 }
46}
47
48impl From<XmlElementPrelim> for XmlIn {
49 #[inline]
50 fn from(value: XmlElementPrelim) -> Self {
51 Self::Element(value)
52 }
53}
54
55impl From<XmlFragmentPrelim> for XmlIn {
56 #[inline]
57 fn from(value: XmlFragmentPrelim) -> Self {
58 Self::Fragment(value)
59 }
60}
61
62impl XmlPrelim for XmlIn {}
63
64impl Prelim for XmlIn {
65 type Return = XmlOut;
66
67 fn into_content(self, _txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
68 let type_ref = match &self {
69 XmlIn::Text(_) => TypeRef::XmlText,
70 XmlIn::Element(prelim) => TypeRef::XmlElement(prelim.tag.clone()),
71 XmlIn::Fragment(_) => TypeRef::XmlFragment,
72 };
73 (ItemContent::Type(Branch::new(type_ref)), Some(self))
74 }
75
76 fn integrate(self, txn: &mut TransactionMut, inner_ref: BranchPtr) {
77 match self {
78 XmlIn::Text(prelim) => prelim.integrate(txn, inner_ref),
79 XmlIn::Element(prelim) => prelim.integrate(txn, inner_ref),
80 XmlIn::Fragment(prelim) => prelim.integrate(txn, inner_ref),
81 }
82 }
83}
84
85/// A return type from XML elements retrieval methods. It's an enum of all supported values, that
86/// can be nested inside [XmlElementRef]. These are other [XmlElementRef]s, [XmlFragmentRef]s
87/// or [XmlTextRef] values.
88#[derive(Debug, Clone)]
89pub enum XmlOut {
90 Element(XmlElementRef),
91 Fragment(XmlFragmentRef),
92 Text(XmlTextRef),
93}
94
95impl XmlOut {
96 pub fn as_ptr(&self) -> BranchPtr {
97 match self {
98 XmlOut::Element(n) => n.0,
99 XmlOut::Fragment(n) => n.0,
100 XmlOut::Text(n) => n.0,
101 }
102 }
103
104 pub fn id(&self) -> BranchID {
105 self.as_ptr().id()
106 }
107
108 /// If current underlying [XmlOut] is wrapping a [XmlElementRef], it will be returned.
109 /// Otherwise, a `None` will be returned.
110 pub fn into_xml_element(self) -> Option<XmlElementRef> {
111 match self {
112 XmlOut::Element(n) => Some(n),
113 _ => None,
114 }
115 }
116
117 /// If current underlying [XmlOut] is wrapping a [XmlFragmentRef], it will be returned.
118 /// Otherwise, a `None` will be returned.
119 pub fn into_xml_fragment(self) -> Option<XmlFragmentRef> {
120 match self {
121 XmlOut::Fragment(n) => Some(n),
122 _ => None,
123 }
124 }
125
126 /// If current underlying [XmlOut] is wrapping a [XmlTextRef], it will be returned.
127 /// Otherwise, a `None` will be returned.
128 pub fn into_xml_text(self) -> Option<XmlTextRef> {
129 match self {
130 XmlOut::Text(n) => Some(n),
131 _ => None,
132 }
133 }
134}
135
136impl AsRef<Branch> for XmlOut {
137 fn as_ref(&self) -> &Branch {
138 match self {
139 XmlOut::Element(n) => n.as_ref(),
140 XmlOut::Fragment(n) => n.as_ref(),
141 XmlOut::Text(n) => n.as_ref(),
142 }
143 }
144}
145
146impl TryInto<XmlElementRef> for XmlOut {
147 type Error = XmlOut;
148
149 fn try_into(self) -> Result<XmlElementRef, Self::Error> {
150 match self {
151 XmlOut::Element(xml) => Ok(xml),
152 other => Err(other),
153 }
154 }
155}
156
157impl TryInto<XmlTextRef> for XmlOut {
158 type Error = XmlOut;
159
160 fn try_into(self) -> Result<XmlTextRef, Self::Error> {
161 match self {
162 XmlOut::Text(xml) => Ok(xml),
163 other => Err(other),
164 }
165 }
166}
167
168impl TryInto<XmlFragmentRef> for XmlOut {
169 type Error = XmlOut;
170
171 fn try_into(self) -> Result<XmlFragmentRef, Self::Error> {
172 match self {
173 XmlOut::Fragment(xml) => Ok(xml),
174 other => Err(other),
175 }
176 }
177}
178
179impl TryFrom<BranchPtr> for XmlOut {
180 type Error = BranchPtr;
181
182 fn try_from(value: BranchPtr) -> Result<Self, Self::Error> {
183 match value.type_ref {
184 TypeRef::XmlElement(_) => Ok(XmlOut::Element(XmlElementRef::from(value))),
185 TypeRef::XmlFragment => Ok(XmlOut::Fragment(XmlFragmentRef::from(value))),
186 TypeRef::XmlText => Ok(XmlOut::Text(XmlTextRef::from(value))),
187 _ => Err(value),
188 }
189 }
190}
191
192impl TryFrom<Out> for XmlOut {
193 type Error = Out;
194
195 fn try_from(value: Out) -> Result<Self, Self::Error> {
196 match value {
197 Out::YXmlElement(n) => Ok(XmlOut::Element(n)),
198 Out::YXmlFragment(n) => Ok(XmlOut::Fragment(n)),
199 Out::YXmlText(n) => Ok(XmlOut::Text(n)),
200 other => Err(other),
201 }
202 }
203}
204
205impl From<XmlOut> for Out {
206 fn from(value: XmlOut) -> Self {
207 match value {
208 XmlOut::Element(xml) => Out::YXmlElement(xml),
209 XmlOut::Fragment(xml) => Out::YXmlFragment(xml),
210 XmlOut::Text(xml) => Out::YXmlText(xml),
211 }
212 }
213}
214
215impl TryFrom<ItemPtr> for XmlOut {
216 type Error = ItemPtr;
217
218 fn try_from(value: ItemPtr) -> Result<Self, Self::Error> {
219 if let Some(branch) = value.clone().as_branch() {
220 match branch.type_ref {
221 TypeRef::XmlElement(_) => Ok(XmlOut::Element(XmlElementRef::from(branch))),
222 TypeRef::XmlFragment => Ok(XmlOut::Fragment(XmlFragmentRef::from(branch))),
223 TypeRef::XmlText => Ok(XmlOut::Text(XmlTextRef::from(branch))),
224 _ => return Err(value),
225 }
226 } else {
227 Err(value)
228 }
229 }
230}
231
232/// XML element data type. It represents an XML node, which can contain key-value attributes
233/// (interpreted as strings) as well as other nested XML elements or rich text (represented by
234/// [XmlTextRef] type).
235///
236/// In terms of conflict resolution, [XmlElementRef] uses following rules:
237///
238/// - Attribute updates use logical last-write-wins principle, meaning the past updates are
239/// automatically overridden and discarded by newer ones, while concurrent updates made by
240/// different peers are resolved into a single value using document id seniority to establish
241/// an order.
242/// - Child node insertion uses sequencing rules from other Yrs collections - elements are inserted
243/// using interleave-resistant algorithm, where order of concurrent inserts at the same index
244/// is established using peer's document id seniority.
245#[repr(transparent)]
246#[derive(Debug, Clone)]
247pub struct XmlElementRef(BranchPtr);
248
249impl SharedRef for XmlElementRef {}
250impl Xml for XmlElementRef {}
251impl XmlFragment for XmlElementRef {}
252impl IndexedSequence for XmlElementRef {}
253
254impl AsRef<XmlFragmentRef> for XmlElementRef {
255 #[inline]
256 fn as_ref(&self) -> &XmlFragmentRef {
257 unsafe { std::mem::transmute(self) }
258 }
259}
260
261impl AsRef<ArrayRef> for XmlElementRef {
262 #[inline]
263 fn as_ref(&self) -> &ArrayRef {
264 unsafe { std::mem::transmute(self) }
265 }
266}
267
268impl XmlElementRef {
269 /// A tag name of a current top-level XML node, eg. node `<p></p>` has "p" as it's tag name.
270 pub fn try_tag(&self) -> Option<&Arc<str>> {
271 if let TypeRef::XmlElement(tag) = &self.0.type_ref {
272 Some(tag)
273 } else {
274 // this could happen only if we reinterpret top level type as XmlElementRef
275 None
276 }
277 }
278
279 /// A tag name of a current top-level XML node, eg. node `<p></p>` has "p" as it's tag name.
280 pub fn tag(&self) -> &Arc<str> {
281 self.try_tag().expect("XmlElement tag was not defined")
282 }
283}
284
285impl GetString for XmlElementRef {
286 /// Converts current XML node into a textual representation. This representation if flat, it
287 /// doesn't include any indentation.
288 fn get_string<T: ReadTxn>(&self, txn: &T) -> String {
289 let tag: &str = self.tag();
290 let inner = self.0;
291 let mut s = String::new();
292 write!(&mut s, "<{}", tag).unwrap();
293 let attributes = Attributes(inner.entries(txn));
294 for (k, v) in attributes {
295 write!(&mut s, " {}=\"{}\"", k, v).unwrap();
296 }
297 write!(&mut s, ">").unwrap();
298 for i in inner.iter(txn) {
299 if !i.is_deleted() {
300 for content in i.content.get_content() {
301 write!(&mut s, "{}", content.to_string(txn)).unwrap();
302 }
303 }
304 }
305 write!(&mut s, "</{}>", tag).unwrap();
306 s
307 }
308}
309
310impl DeepObservable for XmlElementRef {}
311impl Observable for XmlElementRef {
312 type Event = XmlEvent;
313}
314
315impl AsRef<Branch> for XmlElementRef {
316 fn as_ref(&self) -> &Branch {
317 &self.0
318 }
319}
320
321impl Eq for XmlElementRef {}
322impl PartialEq for XmlElementRef {
323 fn eq(&self, other: &Self) -> bool {
324 self.0.id() == other.0.id()
325 }
326}
327
328impl From<BranchPtr> for XmlElementRef {
329 fn from(inner: BranchPtr) -> Self {
330 XmlElementRef(inner)
331 }
332}
333
334impl TryFrom<ItemPtr> for XmlElementRef {
335 type Error = ItemPtr;
336
337 fn try_from(value: ItemPtr) -> Result<Self, Self::Error> {
338 if let Some(branch) = value.clone().as_branch() {
339 Ok(Self::from(branch))
340 } else {
341 Err(value)
342 }
343 }
344}
345
346impl TryFrom<Out> for XmlElementRef {
347 type Error = Out;
348
349 fn try_from(value: Out) -> Result<Self, Self::Error> {
350 match value {
351 Out::YXmlElement(value) => Ok(value),
352 other => Err(other),
353 }
354 }
355}
356
357impl AsPrelim for XmlElementRef {
358 type Prelim = XmlElementPrelim;
359
360 fn as_prelim<T: ReadTxn>(&self, txn: &T) -> Self::Prelim {
361 let attributes: HashMap<Arc<str>, String> = self
362 .0
363 .map
364 .iter()
365 .flat_map(|(k, ptr)| {
366 if ptr.is_deleted() {
367 None
368 } else if let Some(value) = ptr.content.get_last() {
369 Some((k.clone(), value.to_string(txn)))
370 } else {
371 None
372 }
373 })
374 .collect();
375 let children: Vec<_> = self
376 .children(txn)
377 .map(|v| match v {
378 XmlOut::Element(v) => XmlIn::Element(v.as_prelim(txn)),
379 XmlOut::Fragment(v) => XmlIn::Fragment(v.as_prelim(txn)),
380 XmlOut::Text(v) => XmlIn::Text(v.as_prelim(txn)),
381 })
382 .collect();
383 XmlElementPrelim {
384 tag: self.tag().clone(),
385 attributes,
386 children,
387 }
388 }
389}
390
391/// A preliminary type that will be materialized into an [XmlElementRef] once it will be integrated
392/// into Yrs document.
393#[derive(Debug, Clone, PartialEq)]
394pub struct XmlElementPrelim {
395 pub tag: Arc<str>,
396 pub attributes: HashMap<Arc<str>, String>,
397 pub children: Vec<XmlIn>,
398}
399
400impl XmlElementPrelim {
401 pub fn new<S, I>(tag: S, iter: I) -> Self
402 where
403 S: Into<Arc<str>>,
404 I: IntoIterator<Item = XmlIn>,
405 {
406 XmlElementPrelim {
407 tag: tag.into(),
408 attributes: HashMap::default(),
409 children: iter.into_iter().collect(),
410 }
411 }
412
413 pub fn empty<S>(tag: S) -> Self
414 where
415 S: Into<Arc<str>>,
416 {
417 XmlElementPrelim {
418 tag: tag.into(),
419 attributes: HashMap::default(),
420 children: Vec::default(),
421 }
422 }
423}
424
425impl XmlPrelim for XmlElementPrelim {}
426
427impl Prelim for XmlElementPrelim {
428 type Return = XmlElementRef;
429
430 fn into_content(self, _txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
431 let inner = Branch::new(TypeRef::XmlElement(self.tag.clone()));
432 (ItemContent::Type(inner), Some(self))
433 }
434
435 fn integrate(self, txn: &mut TransactionMut, inner_ref: BranchPtr) {
436 let xml = XmlElementRef::from(inner_ref);
437 for (key, value) in self.attributes {
438 xml.insert_attribute(txn, key, value);
439 }
440 for value in self.children {
441 xml.push_back(txn, value);
442 }
443 }
444}
445
446impl Into<EmbedPrelim<XmlElementPrelim>> for XmlElementPrelim {
447 #[inline]
448 fn into(self) -> EmbedPrelim<XmlElementPrelim> {
449 EmbedPrelim::Shared(self)
450 }
451}
452
453impl From<XmlElementPrelim> for In {
454 #[inline]
455 fn from(value: XmlElementPrelim) -> Self {
456 In::XmlElement(value)
457 }
458}
459
460/// A shared data type used for collaborative text editing, that can be used in a context of
461/// [XmlElementRef] node. It enables multiple users to add and remove chunks of text in efficient
462/// manner. This type is internally represented as a mutable double-linked list of text chunks
463/// - an optimization occurs during [Transaction::commit], which allows to squash multiple
464/// consecutively inserted characters together as a single chunk of text even between transaction
465/// boundaries in order to preserve more efficient memory model.
466///
467/// Just like [XmlElementRef], [XmlTextRef] can be marked with extra metadata in form of attributes.
468///
469/// [XmlTextRef] structure internally uses UTF-8 encoding and its length is described in a number of
470/// bytes rather than individual characters (a single UTF-8 code point can consist of many bytes).
471///
472/// Like all Yrs shared data types, [XmlTextRef] is resistant to the problem of interleaving (situation
473/// when characters inserted one after another may interleave with other peers concurrent inserts
474/// after merging all updates together). In case of Yrs conflict resolution is solved by using
475/// unique document id to determine correct and consistent ordering.
476///
477/// [XmlTextRef] offers a rich text editing capabilities (it's not limited to simple text operations).
478/// Actions like embedding objects, binaries (eg. images) and formatting attributes are all possible
479/// using [XmlTextRef].
480///
481/// Keep in mind that [XmlTextRef::get_string] method returns a raw string, while rendering
482/// formatting attrbitues as XML tags in-text. However it doesn't include embedded elements.
483/// If there's a need to include them, use [XmlTextRef::diff] method instead.
484///
485/// Another note worth reminding is that human-readable numeric indexes are not good for maintaining
486/// cursor positions in rich text documents with real-time collaborative capabilities. In such cases
487/// any concurrent update incoming and applied from the remote peer may change the order of elements
488/// in current [XmlTextRef], invalidating numeric index. For such cases you can take advantage of fact
489/// that [XmlTextRef] implements [IndexedSequence::sticky_index] method that returns a
490/// [permanent index](crate::StickyIndex) position that sticks to the same place even when concurrent
491/// updates are being made.
492///
493/// # Example
494///
495/// ```rust
496/// use yrs::{Any, Array, ArrayPrelim, Doc, GetString, Text, Transact, WriteTxn, XmlFragment, XmlTextPrelim};
497/// use yrs::types::Attrs;
498///
499/// let doc = Doc::new();
500/// let mut txn = doc.transact_mut();
501/// let f = txn.get_or_insert_xml_fragment("article");
502/// let text = f.insert(&mut txn, 0, XmlTextPrelim::new(""));
503///
504/// let bold = Attrs::from([("b".into(), true.into())]);
505/// let italic = Attrs::from([("i".into(), true.into())]);
506///
507/// text.insert(&mut txn, 0, "hello ");
508/// text.insert_with_attributes(&mut txn, 6, "world", italic);
509/// text.format(&mut txn, 0, 5, bold);
510///
511/// assert_eq!(text.get_string(&txn), "<b>hello</b> <i>world</i>");
512///
513/// // remove formatting
514/// let remove_italic = Attrs::from([("i".into(), Any::Null)]);
515/// text.format(&mut txn, 6, 5, remove_italic);
516///
517/// assert_eq!(text.get_string(&txn), "<b>hello</b> world");
518///
519/// // insert binary payload eg. images
520/// let image = b"deadbeaf".to_vec();
521/// text.insert_embed(&mut txn, 1, image);
522///
523/// // insert nested shared type eg. table as ArrayRef of ArrayRefs
524/// let table = text.insert_embed(&mut txn, 5, ArrayPrelim::default());
525/// let header = table.insert(&mut txn, 0, ArrayPrelim::from(["Book title", "Author"]));
526/// let row = table.insert(&mut txn, 1, ArrayPrelim::from(["\"Moby-Dick\"", "Herman Melville"]));
527/// ```
528#[repr(transparent)]
529#[derive(Debug, Clone)]
530pub struct XmlTextRef(BranchPtr);
531
532impl XmlTextRef {
533 pub(crate) fn get_string_fragment(
534 head: Option<ItemPtr>,
535 start: Option<&StickyIndex>,
536 end: Option<&StickyIndex>,
537 ) -> String {
538 let mut buf = String::new();
539 for d in diff_between(head, start, end, YChange::identity) {
540 let mut attrs = Vec::new();
541 if let Some(attributes) = d.attributes.as_ref() {
542 for (key, value) in attributes.iter() {
543 attrs.push((key, value));
544 }
545 attrs.sort_by(|x, y| x.0.cmp(y.0))
546 }
547
548 // write attributes as xml opening tags
549 for (node, at) in attrs.iter() {
550 write!(buf, "<{}", node).unwrap();
551 if let Any::Map(at) = at {
552 for (k, v) in at.iter() {
553 write!(buf, " {}=\"{}\"", k, v).unwrap();
554 }
555 }
556 buf.push('>');
557 }
558
559 // write string content of delta
560 if let Out::Any(any) = d.insert {
561 write!(buf, "{}", any).unwrap();
562 }
563
564 // write attributes as xml closing tags
565 attrs.reverse();
566 for (key, _) in attrs {
567 write!(buf, "</{}>", key).unwrap();
568 }
569 }
570 buf
571 }
572}
573
574impl SharedRef for XmlTextRef {}
575impl Xml for XmlTextRef {}
576impl Text for XmlTextRef {}
577impl IndexedSequence for XmlTextRef {}
578#[cfg(feature = "weak")]
579impl crate::Quotable for XmlTextRef {}
580
581impl AsRef<TextRef> for XmlTextRef {
582 #[inline]
583 fn as_ref(&self) -> &TextRef {
584 unsafe { std::mem::transmute(self) }
585 }
586}
587
588impl DeepObservable for XmlTextRef {}
589impl Observable for XmlTextRef {
590 type Event = XmlTextEvent;
591}
592
593impl GetString for XmlTextRef {
594 fn get_string<T: ReadTxn>(&self, _txn: &T) -> String {
595 XmlTextRef::get_string_fragment(self.0.start, None, None)
596 }
597}
598
599impl AsRef<Branch> for XmlTextRef {
600 fn as_ref(&self) -> &Branch {
601 &self.0
602 }
603}
604
605impl Eq for XmlTextRef {}
606impl PartialEq for XmlTextRef {
607 fn eq(&self, other: &Self) -> bool {
608 self.0.id() == other.0.id()
609 }
610}
611
612impl From<BranchPtr> for XmlTextRef {
613 fn from(inner: BranchPtr) -> Self {
614 XmlTextRef(inner)
615 }
616}
617
618impl TryFrom<ItemPtr> for XmlTextRef {
619 type Error = ItemPtr;
620
621 fn try_from(value: ItemPtr) -> Result<Self, Self::Error> {
622 if let Some(branch) = value.clone().as_branch() {
623 Ok(Self::from(branch))
624 } else {
625 Err(value)
626 }
627 }
628}
629
630impl TryFrom<Out> for XmlTextRef {
631 type Error = Out;
632
633 fn try_from(value: Out) -> Result<Self, Self::Error> {
634 match value {
635 Out::YXmlText(value) => Ok(value),
636 other => Err(other),
637 }
638 }
639}
640
641impl AsPrelim for XmlTextRef {
642 type Prelim = XmlDeltaPrelim;
643
644 fn as_prelim<T: ReadTxn>(&self, txn: &T) -> Self::Prelim {
645 let attributes: HashMap<Arc<str>, String> = self
646 .0
647 .map
648 .iter()
649 .flat_map(|(k, ptr)| {
650 if ptr.is_deleted() {
651 None
652 } else if let Some(value) = ptr.content.get_last() {
653 Some((k.clone(), value.to_string(txn)))
654 } else {
655 None
656 }
657 })
658 .collect();
659 let delta: Vec<Delta<In>> = self
660 .diff(txn, YChange::identity)
661 .into_iter()
662 .map(|diff| Delta::Inserted(diff.insert.as_prelim(txn), diff.attributes))
663 .collect();
664 XmlDeltaPrelim { attributes, delta }
665 }
666}
667
668impl DefaultPrelim for XmlTextRef {
669 type Prelim = XmlTextPrelim;
670
671 #[inline]
672 fn default_prelim() -> Self::Prelim {
673 XmlTextPrelim::default()
674 }
675}
676
677/// A preliminary type that will be materialized into an [XmlTextRef] once it will be integrated
678/// into Yrs document.
679#[repr(transparent)]
680#[derive(Debug, Clone, PartialEq, Hash, Default)]
681pub struct XmlTextPrelim(String);
682
683impl Deref for XmlTextPrelim {
684 type Target = String;
685
686 #[inline]
687 fn deref(&self) -> &Self::Target {
688 &self.0
689 }
690}
691
692impl DerefMut for XmlTextPrelim {
693 #[inline]
694 fn deref_mut(&mut self) -> &mut Self::Target {
695 &mut self.0
696 }
697}
698
699impl XmlTextPrelim {
700 #[inline]
701 pub fn new<S: Into<String>>(str: S) -> Self {
702 XmlTextPrelim(str.into())
703 }
704}
705
706impl XmlPrelim for XmlTextPrelim {}
707
708impl Prelim for XmlTextPrelim {
709 type Return = XmlTextRef;
710
711 fn into_content(self, _txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
712 let inner = Branch::new(TypeRef::XmlText);
713 (ItemContent::Type(inner), Some(self))
714 }
715
716 fn integrate(self, txn: &mut TransactionMut, inner_ref: BranchPtr) {
717 if !self.is_empty() {
718 let text = XmlTextRef::from(inner_ref);
719 text.push(txn, &self.0);
720 }
721 }
722}
723
724impl Into<EmbedPrelim<XmlTextPrelim>> for XmlTextPrelim {
725 #[inline]
726 fn into(self) -> EmbedPrelim<XmlTextPrelim> {
727 EmbedPrelim::Shared(self)
728 }
729}
730
731impl From<XmlTextPrelim> for In {
732 #[inline]
733 fn from(value: XmlTextPrelim) -> Self {
734 In::XmlText(XmlDeltaPrelim::from(value))
735 }
736}
737
738#[derive(Debug, Clone, PartialEq, Default)]
739pub struct XmlDeltaPrelim {
740 pub attributes: HashMap<Arc<str>, String>,
741 pub delta: Vec<Delta<In>>,
742}
743
744impl Deref for XmlDeltaPrelim {
745 type Target = [Delta<In>];
746
747 #[inline]
748 fn deref(&self) -> &Self::Target {
749 &self.delta
750 }
751}
752
753impl Prelim for XmlDeltaPrelim {
754 type Return = XmlTextRef;
755
756 fn into_content(self, _txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
757 (ItemContent::Type(Branch::new(TypeRef::XmlText)), Some(self))
758 }
759
760 fn integrate(self, txn: &mut TransactionMut, inner_ref: BranchPtr) {
761 let text_ref = XmlTextRef::from(inner_ref);
762 for (key, value) in self.attributes {
763 text_ref.insert_attribute(txn, key, value);
764 }
765 text_ref.apply_delta(txn, self.delta);
766 }
767}
768
769impl From<XmlTextPrelim> for XmlDeltaPrelim {
770 fn from(value: XmlTextPrelim) -> Self {
771 XmlDeltaPrelim {
772 attributes: HashMap::default(),
773 delta: vec![Delta::Inserted(In::Any(Any::from(value.0)), None)],
774 }
775 }
776}
777
778impl From<XmlDeltaPrelim> for In {
779 #[inline]
780 fn from(value: XmlDeltaPrelim) -> Self {
781 In::XmlText(value)
782 }
783}
784
785/// A XML fragment, which works as an untagged collection of XML nodes.
786#[repr(transparent)]
787#[derive(Debug, Clone)]
788pub struct XmlFragmentRef(BranchPtr);
789
790impl RootRef for XmlFragmentRef {
791 fn type_ref() -> TypeRef {
792 TypeRef::XmlFragment
793 }
794}
795impl SharedRef for XmlFragmentRef {}
796impl XmlFragment for XmlFragmentRef {}
797impl IndexedSequence for XmlFragmentRef {}
798
799impl XmlFragmentRef {
800 pub fn parent(&self) -> Option<XmlOut> {
801 let item = self.0.item?;
802 let parent = item.parent.as_branch()?;
803 XmlOut::try_from(*parent).ok()
804 }
805}
806
807impl AsRef<ArrayRef> for XmlFragmentRef {
808 #[inline]
809 fn as_ref(&self) -> &ArrayRef {
810 unsafe { std::mem::transmute(self) }
811 }
812}
813
814impl GetString for XmlFragmentRef {
815 /// Converts current XML node into a textual representation. This representation if flat, it
816 /// doesn't include any indentation.
817 fn get_string<T: ReadTxn>(&self, txn: &T) -> String {
818 let inner = self.0;
819 let mut s = String::new();
820 for i in inner.iter(txn) {
821 if !i.is_deleted() {
822 for content in i.content.get_content() {
823 write!(&mut s, "{}", content.to_string(txn)).unwrap();
824 }
825 }
826 }
827 s
828 }
829}
830
831impl DeepObservable for XmlFragmentRef {}
832impl Observable for XmlFragmentRef {
833 type Event = XmlEvent;
834}
835
836impl AsRef<Branch> for XmlFragmentRef {
837 fn as_ref(&self) -> &Branch {
838 self.0.deref()
839 }
840}
841
842impl Eq for XmlFragmentRef {}
843impl PartialEq for XmlFragmentRef {
844 fn eq(&self, other: &Self) -> bool {
845 self.0.id() == other.0.id()
846 }
847}
848
849impl From<BranchPtr> for XmlFragmentRef {
850 fn from(inner: BranchPtr) -> Self {
851 XmlFragmentRef(inner)
852 }
853}
854
855impl TryFrom<ItemPtr> for XmlFragmentRef {
856 type Error = ItemPtr;
857
858 fn try_from(value: ItemPtr) -> Result<Self, Self::Error> {
859 if let Some(branch) = value.clone().as_branch() {
860 Ok(Self::from(branch))
861 } else {
862 Err(value)
863 }
864 }
865}
866
867impl TryFrom<Out> for XmlFragmentRef {
868 type Error = Out;
869
870 fn try_from(value: Out) -> Result<Self, Self::Error> {
871 match value {
872 Out::YXmlFragment(value) => Ok(value),
873 other => Err(other),
874 }
875 }
876}
877
878impl AsPrelim for XmlFragmentRef {
879 type Prelim = XmlFragmentPrelim;
880
881 fn as_prelim<T: ReadTxn>(&self, txn: &T) -> Self::Prelim {
882 let children: Vec<_> = self
883 .children(txn)
884 .map(|v| match v {
885 XmlOut::Element(v) => XmlIn::from(v.as_prelim(txn)),
886 XmlOut::Fragment(v) => XmlIn::from(v.as_prelim(txn)),
887 XmlOut::Text(v) => XmlIn::from(v.as_prelim(txn)),
888 })
889 .collect();
890 XmlFragmentPrelim(children)
891 }
892}
893
894impl DefaultPrelim for XmlFragmentRef {
895 type Prelim = XmlFragmentPrelim;
896
897 #[inline]
898 fn default_prelim() -> Self::Prelim {
899 XmlFragmentPrelim::default()
900 }
901}
902
903/// A preliminary type that will be materialized into an [XmlFragmentRef] once it will be integrated
904/// into Yrs document.
905#[derive(Debug, Clone, PartialEq, Default)]
906pub struct XmlFragmentPrelim(Vec<XmlIn>);
907
908impl XmlFragmentPrelim {
909 pub fn new<I, T>(iter: I) -> Self
910 where
911 I: IntoIterator<Item = XmlIn>,
912 {
913 XmlFragmentPrelim(iter.into_iter().collect())
914 }
915}
916
917impl Prelim for XmlFragmentPrelim {
918 type Return = XmlFragmentRef;
919
920 fn into_content(self, _txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
921 let inner = Branch::new(TypeRef::XmlFragment);
922 (ItemContent::Type(inner), Some(self))
923 }
924
925 fn integrate(self, txn: &mut TransactionMut, inner_ref: BranchPtr) {
926 let xml = XmlFragmentRef::from(inner_ref);
927 for value in self.0 {
928 xml.push_back(txn, value);
929 }
930 }
931}
932
933impl Into<EmbedPrelim<XmlFragmentPrelim>> for XmlFragmentPrelim {
934 #[inline]
935 fn into(self) -> EmbedPrelim<XmlFragmentPrelim> {
936 EmbedPrelim::Shared(self)
937 }
938}
939
940impl From<XmlFragmentPrelim> for In {
941 #[inline]
942 fn from(value: XmlFragmentPrelim) -> Self {
943 In::XmlFragment(value)
944 }
945}
946
947/// (Obsolete) an Yjs-compatible XML node used for nesting Map elements.
948#[derive(Debug, Clone)]
949pub struct XmlHookRef(BranchPtr);
950
951impl Map for XmlHookRef {}
952
953impl ToJson for XmlHookRef {
954 fn to_json<T: ReadTxn>(&self, txn: &T) -> Any {
955 let map: &MapRef = self.as_ref();
956 map.to_json(txn)
957 }
958}
959
960impl AsRef<Branch> for XmlHookRef {
961 fn as_ref(&self) -> &Branch {
962 self.0.deref()
963 }
964}
965
966impl Eq for XmlHookRef {}
967impl PartialEq for XmlHookRef {
968 fn eq(&self, other: &Self) -> bool {
969 self.0.id() == other.0.id()
970 }
971}
972
973impl From<BranchPtr> for XmlHookRef {
974 fn from(inner: BranchPtr) -> Self {
975 XmlHookRef(inner)
976 }
977}
978
979impl AsRef<MapRef> for XmlHookRef {
980 #[inline]
981 fn as_ref(&self) -> &MapRef {
982 unsafe { std::mem::transmute(self) }
983 }
984}
985
986pub trait Xml: AsRef<Branch> {
987 fn parent(&self) -> Option<XmlOut> {
988 let item = self.as_ref().item?;
989 let parent = item.parent.as_branch()?;
990 XmlOut::try_from(*parent).ok()
991 }
992
993 /// Removes an attribute recognized by an `attr_name` from a current XML element.
994 fn remove_attribute<K>(&self, txn: &mut TransactionMut, attr_name: &K)
995 where
996 K: AsRef<str>,
997 {
998 self.as_ref().remove(txn, attr_name.as_ref());
999 }
1000
1001 /// Inserts an attribute entry into current XML element.
1002 fn insert_attribute<K, V>(&self, txn: &mut TransactionMut, attr_name: K, attr_value: V)
1003 where
1004 K: Into<Arc<str>>,
1005 V: Into<String>,
1006 {
1007 let key = attr_name.into();
1008 let value = attr_value.into();
1009 let pos = {
1010 let branch = self.as_ref();
1011 let left = branch.map.get(&key);
1012 ItemPosition {
1013 parent: BranchPtr::from(branch).into(),
1014 left: left.cloned(),
1015 right: None,
1016 index: 0,
1017 current_attrs: None,
1018 }
1019 };
1020
1021 txn.create_item(&pos, value, Some(key));
1022 }
1023
1024 /// Returns a value of an attribute given its `attr_name`. Returns `None` if no such attribute
1025 /// can be found inside of a current XML element.
1026 fn get_attribute<T: ReadTxn>(&self, txn: &T, attr_name: &str) -> Option<String> {
1027 let branch = self.as_ref();
1028 let value = branch.get(txn, attr_name)?;
1029 Some(value.to_string(txn))
1030 }
1031
1032 /// Returns an unordered iterator over all attributes (key-value pairs), that can be found
1033 /// inside of a current XML element.
1034 fn attributes<'a, T: ReadTxn>(&'a self, txn: &'a T) -> Attributes<'a, &'a T, T> {
1035 Attributes(Entries::new(&self.as_ref().map, txn))
1036 }
1037
1038 fn siblings<'a, T: ReadTxn>(&self, txn: &'a T) -> Siblings<'a, T> {
1039 let ptr = BranchPtr::from(self.as_ref());
1040 Siblings::new(ptr.item, txn)
1041 }
1042}
1043
1044pub trait XmlFragment: AsRef<Branch> {
1045 fn first_child(&self) -> Option<XmlOut> {
1046 let first = self.as_ref().first()?;
1047 match &first.content {
1048 ItemContent::Type(c) => {
1049 let ptr = BranchPtr::from(c);
1050 XmlOut::try_from(ptr).ok()
1051 }
1052 _ => None,
1053 }
1054 }
1055
1056 /// Returns an iterator over all children of a current XML fragment.
1057 /// It does NOT include nested children of its children - for such cases use [Self::successors]
1058 /// iterator.
1059 fn children<'a, T: ReadTxn>(&self, txn: &'a T) -> XmlNodes<'a, T> {
1060 let iter = BlockIter::new(BranchPtr::from(self.as_ref()));
1061 XmlNodes::new(iter, txn)
1062 }
1063
1064 /// Returns a number of elements stored in current array.
1065 fn len<T: ReadTxn>(&self, _txn: &T) -> u32 {
1066 self.as_ref().len()
1067 }
1068
1069 /// Inserts a `value` at the given `index`. Inserting at index `0` is equivalent to prepending
1070 /// current array with given `value`, while inserting at array length is equivalent to appending
1071 /// that value at the end of it.
1072 ///
1073 /// Using `index` value that's higher than current array length results in panic.
1074 fn insert<V>(&self, txn: &mut TransactionMut, index: u32, xml_node: V) -> V::Return
1075 where
1076 V: XmlPrelim,
1077 {
1078 let ptr = self.as_ref().insert_at(txn, index, xml_node).unwrap(); // XML node is never empty
1079 if let Ok(integrated) = V::Return::try_from(ptr) {
1080 integrated
1081 } else {
1082 panic!("Defect: inserted XML element returned primitive value block")
1083 }
1084 }
1085
1086 /// Inserts given `value` at the end of the current array.
1087 fn push_back<V>(&self, txn: &mut TransactionMut, xml_node: V) -> V::Return
1088 where
1089 V: XmlPrelim,
1090 {
1091 let len = self.len(txn);
1092 self.insert(txn, len, xml_node)
1093 }
1094
1095 /// Inserts given `value` at the beginning of the current array.
1096 fn push_front<V>(&self, txn: &mut TransactionMut, xml_node: V) -> V::Return
1097 where
1098 V: XmlPrelim,
1099 {
1100 self.insert(txn, 0, xml_node)
1101 }
1102
1103 /// Removes a single element at provided `index`.
1104 fn remove(&self, txn: &mut TransactionMut, index: u32) {
1105 self.remove_range(txn, index, 1)
1106 }
1107
1108 /// Removes a range of elements from current array, starting at given `index` up until
1109 /// a particular number described by `len` has been deleted. This method panics in case when
1110 /// not all expected elements were removed (due to insufficient number of elements in an array)
1111 /// or `index` is outside the bounds of an array.
1112 fn remove_range(&self, txn: &mut TransactionMut, index: u32, len: u32) {
1113 let mut walker = BlockIter::new(BranchPtr::from(self.as_ref()));
1114 if walker.try_forward(txn, index) {
1115 walker.delete(txn, len)
1116 } else {
1117 panic!("Index {} is outside of the range of an array", index);
1118 }
1119 }
1120
1121 /// Retrieves a value stored at a given `index`. Returns `None` when provided index was out
1122 /// of the range of a current array.
1123 fn get<T: ReadTxn>(&self, _txn: &T, index: u32) -> Option<XmlOut> {
1124 let branch = self.as_ref();
1125 let (content, _) = branch.get_at(index)?;
1126 if let ItemContent::Type(inner) = content {
1127 let ptr: BranchPtr = inner.into();
1128 XmlOut::try_from(ptr).ok()
1129 } else {
1130 None
1131 }
1132 }
1133
1134 /// Returns an iterator that can be used to traverse over the successors of a current
1135 /// XML element. This includes recursive step over children of its children. The recursive
1136 /// iteration is depth-first.
1137 ///
1138 /// Example:
1139 /// ```
1140 /// /* construct node with a shape:
1141 /// <div>
1142 /// <p>Hello <b>world</b></p>
1143 /// again
1144 /// </div>
1145 /// */
1146 /// use yrs::{Doc, Text, Xml, XmlOut, Transact, XmlFragment, XmlElementPrelim, XmlTextPrelim, GetString};
1147 ///
1148 /// let doc = Doc::new();
1149 /// let mut html = doc.get_or_insert_xml_fragment("div");
1150 /// let mut txn = doc.transact_mut();
1151 /// let p = html.push_back(&mut txn, XmlElementPrelim::empty("p"));
1152 /// let txt = p.push_back(&mut txn, XmlTextPrelim::new("Hello "));
1153 /// let b = p.push_back(&mut txn, XmlElementPrelim::empty("b"));
1154 /// let txt = b.push_back(&mut txn, XmlTextPrelim::new("world"));
1155 /// let txt = html.push_back(&mut txn, XmlTextPrelim::new("again"));
1156 ///
1157 /// let mut result = Vec::new();
1158 /// for node in html.successors(&txn) {
1159 /// let value = match node {
1160 /// XmlOut::Element(elem) => elem.tag().to_string(),
1161 /// XmlOut::Text(txt) => txt.get_string(&txn),
1162 /// _ => panic!("shouldn't be the case here")
1163 /// };
1164 /// result.push(value);
1165 /// }
1166 /// assert_eq!(result, vec![
1167 /// "p".to_string(),
1168 /// "Hello ".to_string(),
1169 /// "b".to_string(),
1170 /// "world".to_string(),
1171 /// "again".to_string()
1172 /// ]);
1173 /// ```
1174 fn successors<'a, T: ReadTxn>(&'a self, txn: &'a T) -> TreeWalker<'a, &'a T, T> {
1175 TreeWalker::new(self.as_ref(), txn)
1176 }
1177}
1178
1179/// Iterator over the attributes (key-value pairs represented as a strings) of an [XmlElement].
1180pub struct Attributes<'a, B, T>(Entries<'a, B, T>);
1181
1182impl<'a, B, T> Attributes<'a, B, T>
1183where
1184 B: Borrow<T>,
1185 T: ReadTxn,
1186{
1187 pub fn new(branch: &'a Branch, txn: B) -> Self {
1188 let entries = Entries::new(&branch.map, txn);
1189 Attributes(entries)
1190 }
1191}
1192
1193impl<'a, B, T> Iterator for Attributes<'a, B, T>
1194where
1195 B: Borrow<T>,
1196 T: ReadTxn,
1197{
1198 type Item = (&'a str, String);
1199
1200 fn next(&mut self) -> Option<Self::Item> {
1201 let (key, block) = self.0.next()?;
1202 let txn = self.0.txn.borrow();
1203 let value = block
1204 .content
1205 .get_last()
1206 .map(|v| v.to_string(txn))
1207 .unwrap_or(String::default());
1208 Some((key.as_ref(), value))
1209 }
1210}
1211
1212pub struct XmlNodes<'a, T> {
1213 iter: BlockIter,
1214 txn: &'a T,
1215}
1216
1217impl<'a, T: ReadTxn> XmlNodes<'a, T> {
1218 fn new(iter: BlockIter, txn: &'a T) -> Self {
1219 XmlNodes { iter, txn }
1220 }
1221}
1222
1223impl<'a, T: ReadTxn> Iterator for XmlNodes<'a, T> {
1224 type Item = XmlOut;
1225
1226 fn next(&mut self) -> Option<Self::Item> {
1227 let value = self.iter.read_value(self.txn)?;
1228 XmlOut::try_from(value).ok()
1229 }
1230}
1231
1232/// An iterator over [XmlElement] successors, working in a recursive depth-first manner.
1233pub struct TreeWalker<'a, B, T> {
1234 current: Option<&'a Item>,
1235 root: TypePtr,
1236 first_call: bool,
1237 _txn: B,
1238 _marker: PhantomData<T>,
1239}
1240
1241impl<'a, B, T: ReadTxn> TreeWalker<'a, B, T>
1242where
1243 B: Borrow<T>,
1244 T: ReadTxn,
1245{
1246 pub fn new(root: &'a Branch, txn: B) -> Self {
1247 TreeWalker {
1248 current: root.start.as_deref(),
1249 root: TypePtr::Branch(BranchPtr::from(root)),
1250 first_call: true,
1251 _txn: txn,
1252 _marker: PhantomData::default(),
1253 }
1254 }
1255}
1256
1257impl<'a, B, T: ReadTxn> Iterator for TreeWalker<'a, B, T>
1258where
1259 B: Borrow<T>,
1260 T: ReadTxn,
1261{
1262 type Item = XmlOut;
1263
1264 /// Tree walker used depth-first search to move over the xml tree.
1265 fn next(&mut self) -> Option<Self::Item> {
1266 fn try_descend(item: &Item) -> Option<&Item> {
1267 if let ItemContent::Type(t) = &item.content {
1268 let inner = t.as_ref();
1269 match inner.type_ref() {
1270 TypeRef::XmlElement(_) | TypeRef::XmlFragment if !item.is_deleted() => {
1271 return inner.start.as_deref();
1272 }
1273 _ => { /* do nothing */ }
1274 }
1275 }
1276
1277 None
1278 }
1279
1280 let mut result = None;
1281 let mut n = self.current.take();
1282 if let Some(current) = n {
1283 if !self.first_call || current.is_deleted() {
1284 while {
1285 if let Some(current) = n {
1286 if let Some(ptr) = try_descend(current) {
1287 // depth-first search - try walk down the tree first
1288 n = Some(ptr);
1289 } else {
1290 // walk right or up in the tree
1291 while let Some(current) = n {
1292 if let Some(right) = current.right.as_ref() {
1293 n = Some(right);
1294 break;
1295 } else if current.parent == self.root {
1296 n = None;
1297 } else {
1298 let ptr = current.parent.as_branch().unwrap();
1299 n = ptr.item.as_deref();
1300 }
1301 }
1302 }
1303 }
1304 if let Some(current) = n {
1305 current.is_deleted()
1306 } else {
1307 false
1308 }
1309 } {}
1310 }
1311 self.first_call = false;
1312 self.current = n;
1313 }
1314 if let Some(current) = self.current {
1315 if let ItemContent::Type(t) = &current.content {
1316 result = XmlOut::try_from(BranchPtr::from(t)).ok();
1317 }
1318 }
1319 result
1320 }
1321}
1322
1323/// Event generated by [XmlText::observe] method. Emitted during transaction commit phase.
1324pub struct XmlTextEvent {
1325 pub(crate) current_target: BranchPtr,
1326 target: XmlTextRef,
1327 delta: UnsafeCell<Option<Vec<Delta>>>,
1328 keys: UnsafeCell<Result<HashMap<Arc<str>, EntryChange>, HashSet<Option<Arc<str>>>>>,
1329}
1330
1331impl XmlTextEvent {
1332 pub(crate) fn new(branch_ref: BranchPtr, key_changes: HashSet<Option<Arc<str>>>) -> Self {
1333 let current_target = branch_ref.clone();
1334 let target = XmlTextRef::from(branch_ref);
1335 XmlTextEvent {
1336 target,
1337 current_target,
1338 delta: UnsafeCell::new(None),
1339 keys: UnsafeCell::new(Err(key_changes)),
1340 }
1341 }
1342
1343 /// Returns a [XmlText] instance which emitted this event.
1344 pub fn target(&self) -> &XmlTextRef {
1345 &self.target
1346 }
1347
1348 /// Returns a path from root type down to [XmlText] instance which emitted this event.
1349 pub fn path(&self) -> Path {
1350 Branch::path(self.current_target, self.target.0)
1351 }
1352
1353 /// Returns a summary of text changes made over corresponding [XmlText] collection within
1354 /// bounds of current transaction.
1355 pub fn delta(&self, txn: &TransactionMut) -> &[Delta] {
1356 let delta = unsafe { self.delta.get().as_mut().unwrap() };
1357 delta
1358 .get_or_insert_with(|| TextEvent::get_delta(self.target.0, txn))
1359 .as_slice()
1360 }
1361
1362 /// Returns a summary of attribute changes made over corresponding [XmlText] collection within
1363 /// bounds of current transaction.
1364 pub fn keys(&self, txn: &TransactionMut) -> &HashMap<Arc<str>, EntryChange> {
1365 let keys = unsafe { self.keys.get().as_mut().unwrap() };
1366
1367 match keys {
1368 Ok(keys) => {
1369 return keys;
1370 }
1371 Err(subs) => {
1372 let subs = event_keys(txn, self.target.0, subs);
1373 *keys = Ok(subs);
1374 if let Ok(keys) = keys {
1375 keys
1376 } else {
1377 panic!("Defect: should not happen");
1378 }
1379 }
1380 }
1381 }
1382}
1383
1384pub struct Siblings<'a, T> {
1385 current: Option<ItemPtr>,
1386 _txn: &'a T,
1387}
1388
1389impl<'a, T> Siblings<'a, T> {
1390 fn new(current: Option<ItemPtr>, txn: &'a T) -> Self {
1391 Siblings { current, _txn: txn }
1392 }
1393}
1394
1395impl<'a, T> Iterator for Siblings<'a, T> {
1396 type Item = XmlOut;
1397
1398 fn next(&mut self) -> Option<Self::Item> {
1399 while let Some(item) = self.current.as_deref() {
1400 self.current = item.right;
1401 if let Some(right) = self.current.as_deref() {
1402 if !right.is_deleted() {
1403 if let ItemContent::Type(inner) = &right.content {
1404 let ptr = BranchPtr::from(inner);
1405 return XmlOut::try_from(ptr).ok();
1406 }
1407 }
1408 }
1409 }
1410
1411 None
1412 }
1413}
1414
1415impl<'a, T> DoubleEndedIterator for Siblings<'a, T> {
1416 fn next_back(&mut self) -> Option<Self::Item> {
1417 while let Some(item) = self.current.as_deref() {
1418 self.current = item.left;
1419 if let Some(left) = self.current.as_deref() {
1420 if !left.is_deleted() {
1421 if let ItemContent::Type(inner) = &left.content {
1422 let ptr = BranchPtr::from(inner);
1423 return XmlOut::try_from(ptr).ok();
1424 }
1425 }
1426 }
1427 }
1428
1429 None
1430 }
1431}
1432
1433/// Event generated by [XmlElement::observe] method. Emitted during transaction commit phase.
1434pub struct XmlEvent {
1435 pub(crate) current_target: BranchPtr,
1436 target: XmlOut,
1437 change_set: UnsafeCell<Option<Box<ChangeSet<Change>>>>,
1438 keys: UnsafeCell<Result<HashMap<Arc<str>, EntryChange>, HashSet<Option<Arc<str>>>>>,
1439 children_changed: bool,
1440}
1441
1442impl XmlEvent {
1443 pub(crate) fn new(branch_ref: BranchPtr, key_changes: HashSet<Option<Arc<str>>>) -> Self {
1444 let current_target = branch_ref.clone();
1445 let children_changed = key_changes.iter().any(Option::is_none);
1446 XmlEvent {
1447 target: XmlOut::try_from(branch_ref).unwrap(),
1448 current_target,
1449 change_set: UnsafeCell::new(None),
1450 keys: UnsafeCell::new(Err(key_changes)),
1451 children_changed,
1452 }
1453 }
1454
1455 /// True if any child XML nodes have been changed within bounds of current transaction.
1456 pub fn children_changed(&self) -> bool {
1457 self.children_changed
1458 }
1459
1460 /// Returns a [XmlElement] instance which emitted this event.
1461 pub fn target(&self) -> &XmlOut {
1462 &self.target
1463 }
1464
1465 /// Returns a path from root type down to [XmlElement] instance which emitted this event.
1466 pub fn path(&self) -> Path {
1467 Branch::path(self.current_target, self.target.as_ptr())
1468 }
1469
1470 /// Returns a summary of XML child nodes changed within corresponding [XmlElement] collection
1471 /// within bounds of current transaction.
1472 pub fn delta(&self, txn: &TransactionMut) -> &[Change] {
1473 self.changes(txn).delta.as_slice()
1474 }
1475
1476 /// Returns a collection of block identifiers that have been added within a bounds of
1477 /// current transaction.
1478 pub fn added(&self, txn: &TransactionMut) -> &HashSet<ID> {
1479 &self.changes(txn).added
1480 }
1481
1482 /// Returns a collection of block identifiers that have been removed within a bounds of
1483 /// current transaction.
1484 pub fn deleted(&self, txn: &TransactionMut) -> &HashSet<ID> {
1485 &self.changes(txn).deleted
1486 }
1487
1488 /// Returns a summary of attribute changes made over corresponding [XmlElement] collection
1489 /// within bounds of current transaction.
1490 pub fn keys(&self, txn: &TransactionMut) -> &HashMap<Arc<str>, EntryChange> {
1491 let keys = unsafe { self.keys.get().as_mut().unwrap() };
1492
1493 match keys {
1494 Ok(keys) => keys,
1495 Err(subs) => {
1496 let subs = event_keys(txn, self.target.as_ptr(), subs);
1497 *keys = Ok(subs);
1498 if let Ok(keys) = keys {
1499 keys
1500 } else {
1501 panic!("Defect: should not happen");
1502 }
1503 }
1504 }
1505 }
1506
1507 fn changes(&self, txn: &TransactionMut) -> &ChangeSet<Change> {
1508 let change_set = unsafe { self.change_set.get().as_mut().unwrap() };
1509 change_set
1510 .get_or_insert_with(|| Box::new(event_change_set(txn, self.target.as_ptr().start)))
1511 }
1512}
1513
1514#[cfg(test)]
1515mod test {
1516 use std::collections::HashMap;
1517 use std::sync::Arc;
1518
1519 use arc_swap::ArcSwapOption;
1520
1521 use crate::test_utils::exchange_updates;
1522 use crate::transaction::ReadTxn;
1523 use crate::types::xml::{Xml, XmlFragment, XmlOut};
1524 use crate::types::{Attrs, Change, EntryChange, Out};
1525 use crate::updates::decoder::Decode;
1526 use crate::updates::encoder::{Encoder, EncoderV1};
1527 use crate::{
1528 Any, Doc, GetString, Observable, SharedRef, StateVector, Text, Transact, Update,
1529 XmlElementPrelim, XmlTextPrelim, XmlTextRef,
1530 };
1531
1532 #[test]
1533 fn insert_attribute() {
1534 let d1 = Doc::with_client_id(1);
1535 let f = d1.get_or_insert_xml_fragment("xml");
1536 let mut t1 = d1.transact_mut();
1537 let xml1 = f.push_back(&mut t1, XmlElementPrelim::empty("div"));
1538 xml1.insert_attribute(&mut t1, "height", 10.to_string());
1539 assert_eq!(xml1.get_attribute(&t1, "height"), Some("10".to_string()));
1540
1541 let d2 = Doc::with_client_id(1);
1542 let f = d2.get_or_insert_xml_fragment("xml");
1543 let mut t2 = d2.transact_mut();
1544 let xml2 = f.push_back(&mut t2, XmlElementPrelim::empty("div"));
1545 let u = t1.encode_state_as_update_v1(&StateVector::default());
1546 let u = Update::decode_v1(u.as_slice()).unwrap();
1547 t2.apply_update(u).unwrap();
1548 assert_eq!(xml2.get_attribute(&t2, "height"), Some("10".to_string()));
1549 }
1550
1551 #[test]
1552 fn tree_walker() {
1553 let doc = Doc::with_client_id(1);
1554 let root = doc.get_or_insert_xml_fragment("xml");
1555 let mut txn = doc.transact_mut();
1556 /*
1557 <UNDEFINED>
1558 <p>{txt1}{txt2}</p>
1559 <p></p>
1560 <img/>
1561 </UNDEFINED>
1562 */
1563 let p1 = root.push_back(&mut txn, XmlElementPrelim::empty("p"));
1564 p1.push_back(&mut txn, XmlTextPrelim::new(""));
1565 p1.push_back(&mut txn, XmlTextPrelim::new(""));
1566 let p2 = root.push_back(&mut txn, XmlElementPrelim::empty("p"));
1567 root.push_back(&mut txn, XmlElementPrelim::empty("img"));
1568
1569 let all_paragraphs = root.successors(&txn).filter_map(|n| match n {
1570 XmlOut::Element(e) if e.tag() == &"p".into() => Some(e),
1571 _ => None,
1572 });
1573 let actual: Vec<_> = all_paragraphs.collect();
1574
1575 assert_eq!(
1576 actual.len(),
1577 2,
1578 "query selector should found two paragraphs"
1579 );
1580 assert_eq!(
1581 actual[0].hook(),
1582 p1.hook(),
1583 "query selector found 1st paragraph"
1584 );
1585 assert_eq!(
1586 actual[1].hook(),
1587 p2.hook(),
1588 "query selector found 2nd paragraph"
1589 );
1590 }
1591
1592 #[test]
1593 fn text_attributes() {
1594 let doc = Doc::with_client_id(1);
1595 let f = doc.get_or_insert_xml_fragment("test");
1596 let mut txn = doc.transact_mut();
1597 let txt = f.push_back(&mut txn, XmlTextPrelim::new(""));
1598 txt.insert_attribute(&mut txn, "test", 42.to_string());
1599
1600 assert_eq!(txt.get_attribute(&txn, "test"), Some("42".to_string()));
1601 let actual: Vec<_> = txt.attributes(&txn).collect();
1602 assert_eq!(actual, vec![("test", "42".to_string())]);
1603 }
1604
1605 #[test]
1606 fn siblings() {
1607 let doc = Doc::with_client_id(1);
1608 let root = doc.get_or_insert_xml_fragment("root");
1609 let mut txn = doc.transact_mut();
1610 let first = root.push_back(&mut txn, XmlTextPrelim::new("hello"));
1611 let second = root.push_back(&mut txn, XmlElementPrelim::empty("p"));
1612
1613 assert_eq!(
1614 &first.siblings(&txn).next().unwrap().id(),
1615 second.hook().id(),
1616 "first.next_sibling should point to second"
1617 );
1618 assert_eq!(
1619 &second.siblings(&txn).next_back().unwrap().id(),
1620 first.hook().id(),
1621 "second.prev_sibling should point to first"
1622 );
1623 assert_eq!(
1624 &first.parent().unwrap().id(),
1625 root.hook().id(),
1626 "first.parent should point to root"
1627 );
1628 assert!(root.parent().is_none(), "root parent should not exist");
1629 assert_eq!(
1630 &root.first_child().unwrap().id(),
1631 first.hook().id(),
1632 "root.first_child should point to first"
1633 );
1634 }
1635
1636 #[test]
1637 fn serialization() {
1638 let d1 = Doc::with_client_id(1);
1639 let r1 = d1.get_or_insert_xml_fragment("root");
1640 let mut t1 = d1.transact_mut();
1641 let _first = r1.push_back(&mut t1, XmlTextPrelim::new("hello"));
1642 r1.push_back(&mut t1, XmlElementPrelim::empty("p"));
1643
1644 let expected = "hello<p></p>";
1645 assert_eq!(r1.get_string(&t1), expected);
1646
1647 let u1 = t1.encode_state_as_update_v1(&StateVector::default());
1648
1649 let d2 = Doc::with_client_id(2);
1650 let r2 = d2.get_or_insert_xml_fragment("root");
1651 let mut t2 = d2.transact_mut();
1652
1653 let u1 = Update::decode_v1(u1.as_slice()).unwrap();
1654 t2.apply_update(u1).unwrap();
1655 assert_eq!(r2.get_string(&t2), expected);
1656 }
1657
1658 #[test]
1659 fn serialization_compatibility() {
1660 let d1 = Doc::with_client_id(1);
1661 let r1 = d1.get_or_insert_xml_fragment("root");
1662 let mut t1 = d1.transact_mut();
1663 let _first = r1.push_back(&mut t1, XmlTextPrelim::new("hello"));
1664 r1.push_back(&mut t1, XmlElementPrelim::empty("p"));
1665
1666 /* This binary is result of following Yjs code (matching Rust code above):
1667 ```js
1668 let d1 = new Y.Doc()
1669 d1.clientID = 1
1670 let root = d1.get('root', Y.XmlElement)
1671 let first = new Y.XmlText()
1672 first.insert(0, 'hello')
1673 let second = new Y.XmlElement('p')
1674 root.insert(0, [first,second])
1675
1676 let expected = Y.encodeStateAsUpdate(d1)
1677 ``` */
1678 let expected = &[
1679 1, 3, 1, 0, 7, 1, 4, 114, 111, 111, 116, 6, 4, 0, 1, 0, 5, 104, 101, 108, 108, 111,
1680 135, 1, 0, 3, 1, 112, 0,
1681 ];
1682 let u1 = t1.encode_state_as_update_v1(&StateVector::default());
1683 assert_eq!(u1.as_slice(), expected);
1684 }
1685
1686 #[test]
1687 fn event_observers() {
1688 let d1 = Doc::with_client_id(1);
1689 let f = d1.get_or_insert_xml_fragment("xml");
1690 let xml = f.insert(&mut d1.transact_mut(), 0, XmlElementPrelim::empty("test"));
1691
1692 let d2 = Doc::with_client_id(2);
1693 let f = d2.get_or_insert_xml_fragment("xml");
1694 exchange_updates(&[&d1, &d2]);
1695 let xml2 = f
1696 .get(&d2.transact(), 0)
1697 .unwrap()
1698 .into_xml_element()
1699 .unwrap();
1700
1701 let attributes = Arc::new(ArcSwapOption::default());
1702 let nodes = Arc::new(ArcSwapOption::default());
1703 let attributes_c = attributes.clone();
1704 let nodes_c = nodes.clone();
1705 let _sub = xml.observe(move |txn, e| {
1706 attributes_c.store(Some(Arc::new(e.keys(txn).clone())));
1707 nodes_c.store(Some(Arc::new(e.delta(txn).to_vec())));
1708 });
1709
1710 // insert attribute
1711 {
1712 let mut txn = d1.transact_mut();
1713 xml.insert_attribute(&mut txn, "key1", "value1");
1714 xml.insert_attribute(&mut txn, "key2", "value2");
1715 }
1716 assert!(nodes.swap(None).unwrap().is_empty());
1717 assert_eq!(
1718 attributes.swap(None),
1719 Some(Arc::new(HashMap::from([
1720 (
1721 "key1".into(),
1722 EntryChange::Inserted(Any::String("value1".into()).into())
1723 ),
1724 (
1725 "key2".into(),
1726 EntryChange::Inserted(Any::String("value2".into()).into())
1727 )
1728 ])))
1729 );
1730
1731 // change and remove attribute
1732 {
1733 let mut txn = d1.transact_mut();
1734 xml.insert_attribute(&mut txn, "key1", "value11");
1735 xml.remove_attribute(&mut txn, &"key2");
1736 }
1737 assert!(nodes.swap(None).unwrap().is_empty());
1738 assert_eq!(
1739 attributes.swap(None),
1740 Some(Arc::new(HashMap::from([
1741 (
1742 "key1".into(),
1743 EntryChange::Updated(
1744 Any::String("value1".into()).into(),
1745 Any::String("value11".into()).into()
1746 )
1747 ),
1748 (
1749 "key2".into(),
1750 EntryChange::Removed(Any::String("value2".into()).into())
1751 )
1752 ])))
1753 );
1754
1755 // add xml elements
1756 let (nested_txt, nested_xml) = {
1757 let mut txn = d1.transact_mut();
1758 let txt = xml.insert(&mut txn, 0, XmlTextPrelim::new(""));
1759 let xml2 = xml.insert(&mut txn, 1, XmlElementPrelim::empty("div"));
1760 (txt, xml2)
1761 };
1762 assert_eq!(
1763 nodes.swap(None),
1764 Some(Arc::new(vec![Change::Added(vec![
1765 Out::YXmlText(nested_txt.clone()),
1766 Out::YXmlElement(nested_xml.clone())
1767 ])]))
1768 );
1769 assert_eq!(attributes.swap(None), Some(HashMap::new().into()));
1770
1771 // remove and add
1772 let nested_xml2 = {
1773 let mut txn = d1.transact_mut();
1774 xml.remove_range(&mut txn, 1, 1);
1775 xml.insert(&mut txn, 1, XmlElementPrelim::empty("p"))
1776 };
1777 assert_eq!(
1778 nodes.swap(None),
1779 Some(Arc::new(vec![
1780 Change::Retain(1),
1781 Change::Added(vec![Out::YXmlElement(nested_xml2.clone())]),
1782 Change::Removed(1),
1783 ]))
1784 );
1785 assert_eq!(attributes.swap(None), Some(HashMap::new().into()));
1786
1787 // copy updates over
1788 let attributes = Arc::new(ArcSwapOption::default());
1789 let nodes = Arc::new(ArcSwapOption::default());
1790 let attributes_c = attributes.clone();
1791 let nodes_c = nodes.clone();
1792 let _sub = xml2.observe(move |txn, e| {
1793 attributes_c.store(Some(Arc::new(e.keys(txn).clone())));
1794 nodes_c.store(Some(Arc::new(e.delta(txn).to_vec())));
1795 });
1796
1797 {
1798 let t1 = d1.transact_mut();
1799 let mut t2 = d2.transact_mut();
1800 let sv = t2.state_vector();
1801 let mut encoder = EncoderV1::new();
1802 t1.encode_diff(&sv, &mut encoder);
1803 let update = Update::decode_v1(encoder.to_vec().as_slice()).unwrap();
1804 t2.apply_update(update).unwrap();
1805 }
1806 assert_eq!(
1807 nodes.swap(None),
1808 Some(Arc::new(vec![Change::Added(vec![
1809 Out::YXmlText(nested_txt),
1810 Out::YXmlElement(nested_xml2)
1811 ])]))
1812 );
1813 assert_eq!(
1814 attributes.swap(None),
1815 Some(Arc::new(HashMap::from([(
1816 "key1".into(),
1817 EntryChange::Inserted(Any::String("value11".into()).into())
1818 )])))
1819 );
1820 }
1821
1822 #[test]
1823 fn xml_to_string() {
1824 let doc = Doc::new();
1825 let f = doc.get_or_insert_xml_fragment("test");
1826 let mut txn = doc.transact_mut();
1827 let div = f.push_back(&mut txn, XmlElementPrelim::empty("div"));
1828 div.insert_attribute(&mut txn, "class", "t-button");
1829 let text = div.push_back(&mut txn, XmlTextPrelim::new("hello world"));
1830 text.format(
1831 &mut txn,
1832 6,
1833 5,
1834 Attrs::from([(
1835 "a".into(),
1836 HashMap::from([("href".into(), "http://domain.org")]).into(),
1837 )]),
1838 );
1839 drop(txn);
1840
1841 let str = f.get_string(&doc.transact());
1842 assert_eq!(
1843 str.as_str(),
1844 "<div class=\"t-button\">hello <a href=\"http://domain.org\">world</a></div>"
1845 )
1846 }
1847
1848 #[test]
1849 fn xml_to_string_2() {
1850 let doc = Doc::new();
1851 let f = doc.get_or_insert_xml_fragment("article");
1852 let xml = f.insert(&mut doc.transact_mut(), 0, XmlTextPrelim::new(""));
1853 let mut txn = doc.transact_mut();
1854
1855 let bold = Attrs::from([("b".into(), true.into())]);
1856 let italic = Attrs::from([("i".into(), true.into())]);
1857
1858 xml.insert(&mut txn, 0, "hello ");
1859 xml.insert_with_attributes(&mut txn, 6, "world", italic);
1860 xml.format(&mut txn, 0, 5, bold);
1861
1862 assert_eq!(xml.get_string(&txn), "<b>hello</b> <i>world</i>");
1863
1864 let remove_italic = Attrs::from([("i".into(), Any::Null)]);
1865 xml.format(&mut txn, 6, 5, remove_italic);
1866
1867 assert_eq!(xml.get_string(&txn), "<b>hello</b> world");
1868 }
1869
1870 #[test]
1871 fn format_attributes_decode_compatibility_v1() {
1872 let data = &[
1873 1, 6, 1, 0, 6, 1, 4, 116, 101, 115, 116, 1, 105, 4, 116, 114, 117, 101, 132, 1, 0, 6,
1874 104, 101, 108, 108, 111, 32, 132, 1, 6, 5, 119, 111, 114, 108, 100, 134, 1, 11, 1, 105,
1875 4, 110, 117, 108, 108, 198, 1, 6, 1, 7, 1, 98, 4, 116, 114, 117, 101, 134, 1, 12, 1,
1876 98, 4, 110, 117, 108, 108, 0,
1877 ];
1878 let update = Update::decode_v1(data).unwrap();
1879 let doc = Doc::new();
1880 let txt = doc.get_or_insert_text("test");
1881 let txt: &XmlTextRef = txt.as_ref();
1882 let mut txn = doc.transact_mut();
1883
1884 txn.apply_update(update).unwrap();
1885 assert_eq!(txt.get_string(&txn), "<i>hello </i><b><i>world</i></b>");
1886
1887 let actual = txn.encode_state_as_update_v1(&StateVector::default());
1888 assert_eq!(actual, data);
1889 }
1890
1891 #[test]
1892 fn format_attributes_decode_compatibility_v2() {
1893 let data = &[
1894 0, 3, 0, 3, 1, 2, 65, 5, 5, 0, 12, 10, 74, 12, 1, 14, 9, 6, 0, 132, 1, 134, 0, 198, 0,
1895 134, 26, 19, 116, 101, 115, 116, 105, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108,
1896 100, 105, 98, 98, 4, 1, 6, 5, 65, 1, 1, 1, 0, 0, 1, 6, 0, 120, 126, 120, 126, 0,
1897 ];
1898 let update = Update::decode_v2(data).unwrap();
1899 let doc = Doc::new();
1900 let txt = doc.get_or_insert_text("test");
1901 let txt: &XmlTextRef = txt.as_ref();
1902 let mut txn = doc.transact_mut();
1903
1904 txn.apply_update(update).unwrap();
1905 assert_eq!(txt.get_string(&txn), "<i>hello </i><b><i>world</i></b>");
1906
1907 let actual = txn.encode_state_as_update_v2(&StateVector::default());
1908 assert_eq!(actual, data);
1909 }
1910}
1911