use crate::block::{EmbedPrelim, Item, ItemContent, ItemPosition, ItemPtr, Prelim};
use crate::block_iter::BlockIter;
use crate::transaction::TransactionMut;
use crate::types::text::{diff_between, TextEvent, YChange};
use crate::types::{
event_change_set, event_keys, Branch, BranchPtr, Change, ChangeSet, Delta, Entries,
EntryChange, MapRef, Path, RootRef, SharedRef, ToJson, TypePtr, TypeRef, Value,
};
use crate::{
Any, ArrayRef, BranchID, DeepObservable, GetString, IndexedSequence, Map, Observable, ReadTxn,
StickyIndex, Text, TextRef, ID,
};
use std::borrow::Borrow;
use std::cell::UnsafeCell;
use std::collections::{HashMap, HashSet};
use std::convert::{TryFrom, TryInto};
use std::fmt::Write;
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::Arc;
pub trait XmlPrelim: Prelim {}
#[derive(Debug, Clone)]
pub enum XmlNode {
Element(XmlElementRef),
Fragment(XmlFragmentRef),
Text(XmlTextRef),
}
impl XmlNode {
pub fn as_ptr(&self) -> BranchPtr {
match self {
XmlNode::Element(n) => n.0,
XmlNode::Fragment(n) => n.0,
XmlNode::Text(n) => n.0,
}
}
pub fn id(&self) -> BranchID {
self.as_ptr().id()
}
pub fn into_xml_element(self) -> Option<XmlElementRef> {
match self {
XmlNode::Element(n) => Some(n),
_ => None,
}
}
pub fn into_xml_fragment(self) -> Option<XmlFragmentRef> {
match self {
XmlNode::Fragment(n) => Some(n),
_ => None,
}
}
pub fn into_xml_text(self) -> Option<XmlTextRef> {
match self {
XmlNode::Text(n) => Some(n),
_ => None,
}
}
}
impl AsRef<Branch> for XmlNode {
fn as_ref(&self) -> &Branch {
match self {
XmlNode::Element(n) => n.as_ref(),
XmlNode::Fragment(n) => n.as_ref(),
XmlNode::Text(n) => n.as_ref(),
}
}
}
impl TryInto<XmlElementRef> for XmlNode {
type Error = XmlNode;
fn try_into(self) -> Result<XmlElementRef, Self::Error> {
match self {
XmlNode::Element(xml) => Ok(xml),
other => Err(other),
}
}
}
impl TryInto<XmlTextRef> for XmlNode {
type Error = XmlNode;
fn try_into(self) -> Result<XmlTextRef, Self::Error> {
match self {
XmlNode::Text(xml) => Ok(xml),
other => Err(other),
}
}
}
impl TryInto<XmlFragmentRef> for XmlNode {
type Error = XmlNode;
fn try_into(self) -> Result<XmlFragmentRef, Self::Error> {
match self {
XmlNode::Fragment(xml) => Ok(xml),
other => Err(other),
}
}
}
impl TryFrom<BranchPtr> for XmlNode {
type Error = BranchPtr;
fn try_from(value: BranchPtr) -> Result<Self, Self::Error> {
match value.type_ref {
TypeRef::XmlElement(_) => Ok(XmlNode::Element(XmlElementRef::from(value))),
TypeRef::XmlFragment => Ok(XmlNode::Fragment(XmlFragmentRef::from(value))),
TypeRef::XmlText => Ok(XmlNode::Text(XmlTextRef::from(value))),
_ => Err(value),
}
}
}
impl TryFrom<Value> for XmlNode {
type Error = Value;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::YXmlElement(n) => Ok(XmlNode::Element(n)),
Value::YXmlFragment(n) => Ok(XmlNode::Fragment(n)),
Value::YXmlText(n) => Ok(XmlNode::Text(n)),
other => Err(other),
}
}
}
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct XmlElementRef(BranchPtr);
impl SharedRef for XmlElementRef {}
impl Xml for XmlElementRef {}
impl XmlFragment for XmlElementRef {}
impl IndexedSequence for XmlElementRef {}
impl Into<XmlFragmentRef> for XmlElementRef {
fn into(self) -> XmlFragmentRef {
XmlFragmentRef(self.0)
}
}
impl Into<ArrayRef> for XmlElementRef {
fn into(self) -> ArrayRef {
ArrayRef::from(self.0)
}
}
impl Into<MapRef> for XmlElementRef {
fn into(self) -> MapRef {
MapRef::from(self.0)
}
}
impl XmlElementRef {
pub fn try_tag(&self) -> Option<&Arc<str>> {
if let TypeRef::XmlElement(tag) = &self.0.type_ref {
Some(tag)
} else {
None
}
}
pub fn tag(&self) -> &Arc<str> {
self.try_tag().expect("XmlElement tag was not defined")
}
}
impl GetString for XmlElementRef {
fn get_string<T: ReadTxn>(&self, txn: &T) -> String {
let tag: &str = self.tag();
let inner = self.0;
let mut s = String::new();
write!(&mut s, "<{}", tag).unwrap();
let attributes = Attributes(inner.entries(txn));
for (k, v) in attributes {
write!(&mut s, " {}=\"{}\"", k, v).unwrap();
}
write!(&mut s, ">").unwrap();
for i in inner.iter(txn) {
if !i.is_deleted() {
for content in i.content.get_content() {
write!(&mut s, "{}", content.to_string(txn)).unwrap();
}
}
}
write!(&mut s, "</{}>", tag).unwrap();
s
}
}
impl DeepObservable for XmlElementRef {}
impl Observable for XmlElementRef {
type Event = XmlEvent;
}
impl AsRef<Branch> for XmlElementRef {
fn as_ref(&self) -> &Branch {
&self.0
}
}
impl Eq for XmlElementRef {}
impl PartialEq for XmlElementRef {
fn eq(&self, other: &Self) -> bool {
self.0.id() == other.0.id()
}
}
impl From<BranchPtr> for XmlElementRef {
fn from(inner: BranchPtr) -> Self {
XmlElementRef(inner)
}
}
impl TryFrom<ItemPtr> for XmlElementRef {
type Error = ItemPtr;
fn try_from(value: ItemPtr) -> Result<Self, Self::Error> {
if let Some(branch) = value.clone().as_branch() {
Ok(Self::from(branch))
} else {
Err(value)
}
}
}
impl TryFrom<Value> for XmlElementRef {
type Error = Value;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::YXmlElement(value) => Ok(value),
other => Err(other),
}
}
}
#[derive(Debug, Clone)]
pub struct XmlElementPrelim<I, T>(Arc<str>, I)
where
I: IntoIterator<Item = T>,
T: XmlPrelim;
impl<I, T> XmlElementPrelim<I, T>
where
I: IntoIterator<Item = T>,
T: XmlPrelim,
{
pub fn new<S: Into<Arc<str>>>(tag: S, iter: I) -> Self {
XmlElementPrelim(tag.into(), iter)
}
}
impl XmlElementPrelim<Option<XmlTextPrelim<String>>, XmlTextPrelim<String>> {
pub fn empty<S: Into<Arc<str>>>(tag: S) -> Self {
XmlElementPrelim(tag.into(), None)
}
}
impl<I, T> XmlPrelim for XmlElementPrelim<I, T>
where
I: IntoIterator<Item = T>,
T: XmlPrelim,
{
}
impl<I, T> Prelim for XmlElementPrelim<I, T>
where
I: IntoIterator<Item = T>,
T: XmlPrelim,
{
type Return = XmlElementRef;
fn into_content(self, _txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
let inner = Branch::new(TypeRef::XmlElement(self.0.clone()));
(ItemContent::Type(inner), Some(self))
}
fn integrate(self, txn: &mut TransactionMut, inner_ref: BranchPtr) {
let xml = XmlElementRef::from(inner_ref);
for value in self.1 {
xml.push_back(txn, value);
}
}
}
impl<I, T: Prelim> Into<EmbedPrelim<XmlElementPrelim<I, T>>> for XmlElementPrelim<I, T>
where
I: IntoIterator<Item = T>,
T: XmlPrelim,
{
#[inline]
fn into(self) -> EmbedPrelim<XmlElementPrelim<I, T>> {
EmbedPrelim::Shared(self)
}
}
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct XmlTextRef(BranchPtr);
impl XmlTextRef {
pub(crate) fn get_string_fragment(
head: Option<ItemPtr>,
start: Option<&StickyIndex>,
end: Option<&StickyIndex>,
) -> String {
let mut buf = String::new();
for d in diff_between(head, start, end, YChange::identity) {
let mut attrs = Vec::new();
if let Some(attributes) = d.attributes.as_ref() {
for (key, value) in attributes.iter() {
attrs.push((key, value));
}
attrs.sort_by(|x, y| x.0.cmp(y.0))
}
for (node, at) in attrs.iter() {
write!(buf, "<{}", node).unwrap();
if let Any::Map(at) = at {
for (k, v) in at.iter() {
write!(buf, " {}=\"{}\"", k, v).unwrap();
}
}
buf.push('>');
}
if let Value::Any(any) = d.insert {
write!(buf, "{}", any).unwrap();
}
attrs.reverse();
for (key, _) in attrs {
write!(buf, "</{}>", key).unwrap();
}
}
buf
}
}
impl SharedRef for XmlTextRef {}
impl Xml for XmlTextRef {}
impl Text for XmlTextRef {}
impl IndexedSequence for XmlTextRef {}
#[cfg(feature = "weak")]
impl crate::Quotable for XmlTextRef {}
impl Into<TextRef> for XmlTextRef {
fn into(self) -> TextRef {
TextRef::from(self.0)
}
}
impl DeepObservable for XmlTextRef {}
impl Observable for XmlTextRef {
type Event = XmlTextEvent;
}
impl GetString for XmlTextRef {
fn get_string<T: ReadTxn>(&self, _txn: &T) -> String {
XmlTextRef::get_string_fragment(self.0.start, None, None)
}
}
impl AsRef<Branch> for XmlTextRef {
fn as_ref(&self) -> &Branch {
&self.0
}
}
impl Eq for XmlTextRef {}
impl PartialEq for XmlTextRef {
fn eq(&self, other: &Self) -> bool {
self.0.id() == other.0.id()
}
}
impl From<BranchPtr> for XmlTextRef {
fn from(inner: BranchPtr) -> Self {
XmlTextRef(inner)
}
}
impl TryFrom<ItemPtr> for XmlTextRef {
type Error = ItemPtr;
fn try_from(value: ItemPtr) -> Result<Self, Self::Error> {
if let Some(branch) = value.clone().as_branch() {
Ok(Self::from(branch))
} else {
Err(value)
}
}
}
impl TryFrom<Value> for XmlTextRef {
type Error = Value;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::YXmlText(value) => Ok(value),
other => Err(other),
}
}
}
#[derive(Debug)]
pub struct XmlTextPrelim<T: Borrow<str>>(T);
impl Default for XmlTextPrelim<String> {
fn default() -> Self {
XmlTextPrelim::new(String::default())
}
}
impl<T: Borrow<str>> XmlTextPrelim<T> {
#[inline]
pub fn new(str: T) -> Self {
XmlTextPrelim(str)
}
}
impl<T: Borrow<str>> XmlPrelim for XmlTextPrelim<T> {}
impl<T: Borrow<str>> Prelim for XmlTextPrelim<T> {
type Return = XmlTextRef;
fn into_content(self, _txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
let inner = Branch::new(TypeRef::XmlText);
(ItemContent::Type(inner), Some(self))
}
fn integrate(self, txn: &mut TransactionMut, inner_ref: BranchPtr) {
let s = self.0.borrow();
if !s.is_empty() {
let text = XmlTextRef::from(inner_ref);
text.push(txn, s);
}
}
}
impl<T: Borrow<str>> Into<EmbedPrelim<XmlTextPrelim<T>>> for XmlTextPrelim<T> {
#[inline]
fn into(self) -> EmbedPrelim<XmlTextPrelim<T>> {
EmbedPrelim::Shared(self)
}
}
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct XmlFragmentRef(BranchPtr);
impl RootRef for XmlFragmentRef {
fn type_ref() -> TypeRef {
TypeRef::XmlFragment
}
}
impl SharedRef for XmlFragmentRef {}
impl XmlFragment for XmlFragmentRef {}
impl IndexedSequence for XmlFragmentRef {}
impl XmlFragmentRef {
pub fn parent(&self) -> Option<XmlNode> {
let item = self.as_ref().item?;
let parent = item.parent.as_branch()?;
XmlNode::try_from(*parent).ok()
}
}
impl GetString for XmlFragmentRef {
fn get_string<T: ReadTxn>(&self, txn: &T) -> String {
let inner = self.0;
let mut s = String::new();
for i in inner.iter(txn) {
if !i.is_deleted() {
for content in i.content.get_content() {
write!(&mut s, "{}", content.to_string(txn)).unwrap();
}
}
}
s
}
}
impl DeepObservable for XmlFragmentRef {}
impl Observable for XmlFragmentRef {
type Event = XmlEvent;
}
impl AsRef<Branch> for XmlFragmentRef {
fn as_ref(&self) -> &Branch {
self.0.deref()
}
}
impl Eq for XmlFragmentRef {}
impl PartialEq for XmlFragmentRef {
fn eq(&self, other: &Self) -> bool {
self.0.id() == other.0.id()
}
}
impl From<BranchPtr> for XmlFragmentRef {
fn from(inner: BranchPtr) -> Self {
XmlFragmentRef(inner)
}
}
impl TryFrom<ItemPtr> for XmlFragmentRef {
type Error = ItemPtr;
fn try_from(value: ItemPtr) -> Result<Self, Self::Error> {
if let Some(branch) = value.clone().as_branch() {
Ok(Self::from(branch))
} else {
Err(value)
}
}
}
impl TryFrom<Value> for XmlFragmentRef {
type Error = Value;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::YXmlFragment(value) => Ok(value),
other => Err(other),
}
}
}
#[derive(Debug, Clone)]
pub struct XmlFragmentPrelim<I, T>(I)
where
I: IntoIterator<Item = T>,
T: XmlPrelim;
impl<I, T> XmlFragmentPrelim<I, T>
where
I: IntoIterator<Item = T>,
T: XmlPrelim,
{
pub fn new(iter: I) -> Self {
XmlFragmentPrelim(iter)
}
}
impl<I, T> XmlPrelim for XmlFragmentPrelim<I, T>
where
I: IntoIterator<Item = T>,
T: XmlPrelim,
<T as Prelim>::Return: TryFrom<ItemPtr>,
{
}
impl<I, T> Prelim for XmlFragmentPrelim<I, T>
where
I: IntoIterator<Item = T>,
T: XmlPrelim,
<T as Prelim>::Return: TryFrom<ItemPtr>,
{
type Return = XmlFragmentRef;
fn into_content(self, _txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
let inner = Branch::new(TypeRef::XmlFragment);
(ItemContent::Type(inner), Some(self))
}
fn integrate(self, txn: &mut TransactionMut, inner_ref: BranchPtr) {
let xml = XmlFragmentRef::from(inner_ref);
for value in self.0 {
xml.push_back(txn, value);
}
}
}
impl<I, T: Prelim> Into<EmbedPrelim<XmlFragmentPrelim<I, T>>> for XmlFragmentPrelim<I, T>
where
I: IntoIterator<Item = T>,
T: XmlPrelim,
{
#[inline]
fn into(self) -> EmbedPrelim<XmlFragmentPrelim<I, T>> {
EmbedPrelim::Shared(self)
}
}
#[derive(Debug, Clone)]
pub struct XmlHookRef(BranchPtr);
impl Map for XmlHookRef {}
impl ToJson for XmlHookRef {
fn to_json<T: ReadTxn>(&self, txn: &T) -> Any {
let map: MapRef = self.clone().into();
map.to_json(txn)
}
}
impl AsRef<Branch> for XmlHookRef {
fn as_ref(&self) -> &Branch {
self.0.deref()
}
}
impl Eq for XmlHookRef {}
impl PartialEq for XmlHookRef {
fn eq(&self, other: &Self) -> bool {
self.0.id() == other.0.id()
}
}
impl From<BranchPtr> for XmlHookRef {
fn from(inner: BranchPtr) -> Self {
XmlHookRef(inner)
}
}
impl Into<MapRef> for XmlHookRef {
fn into(self) -> MapRef {
MapRef::from(self.0)
}
}
pub trait Xml: AsRef<Branch> {
fn parent(&self) -> Option<XmlNode> {
let item = self.as_ref().item?;
let parent = item.parent.as_branch()?;
XmlNode::try_from(*parent).ok()
}
fn remove_attribute<K>(&self, txn: &mut TransactionMut, attr_name: &K)
where
K: AsRef<str>,
{
self.as_ref().remove(txn, attr_name.as_ref());
}
fn insert_attribute<K, V>(&self, txn: &mut TransactionMut, attr_name: K, attr_value: V)
where
K: Into<Arc<str>>,
V: Into<String>,
{
let key = attr_name.into();
let value = attr_value.into();
let pos = {
let branch = self.as_ref();
let left = branch.map.get(&key);
ItemPosition {
parent: BranchPtr::from(branch).into(),
left: left.cloned(),
right: None,
index: 0,
current_attrs: None,
}
};
txn.create_item(&pos, value, Some(key));
}
fn get_attribute<T: ReadTxn>(&self, txn: &T, attr_name: &str) -> Option<String> {
let branch = self.as_ref();
let value = branch.get(txn, attr_name)?;
Some(value.to_string(txn))
}
fn attributes<'a, T: ReadTxn>(&'a self, txn: &'a T) -> Attributes<'a, &'a T, T> {
Attributes(Entries::new(&self.as_ref().map, txn))
}
fn siblings<'a, T: ReadTxn>(&self, txn: &'a T) -> Siblings<'a, T> {
let ptr = BranchPtr::from(self.as_ref());
Siblings::new(ptr.item, txn)
}
}
pub trait XmlFragment: AsRef<Branch> {
fn first_child(&self) -> Option<XmlNode> {
let first = self.as_ref().first()?;
match &first.content {
ItemContent::Type(c) => {
let ptr = BranchPtr::from(c);
XmlNode::try_from(ptr).ok()
}
_ => None,
}
}
fn len<T: ReadTxn>(&self, _txn: &T) -> u32 {
self.as_ref().len()
}
fn insert<V>(&self, txn: &mut TransactionMut, index: u32, xml_node: V) -> V::Return
where
V: XmlPrelim,
{
let ptr = self.as_ref().insert_at(txn, index, xml_node);
if let Ok(integrated) = V::Return::try_from(ptr) {
integrated
} else {
panic!("Defect: inserted XML element returned primitive value block")
}
}
fn push_back<V>(&self, txn: &mut TransactionMut, xml_node: V) -> V::Return
where
V: XmlPrelim,
{
let len = self.len(txn);
self.insert(txn, len, xml_node)
}
fn push_front<V>(&self, txn: &mut TransactionMut, xml_node: V) -> V::Return
where
V: XmlPrelim,
{
self.insert(txn, 0, xml_node)
}
fn remove(&self, txn: &mut TransactionMut, index: u32) {
self.remove_range(txn, index, 1)
}
fn remove_range(&self, txn: &mut TransactionMut, index: u32, len: u32) {
let mut walker = BlockIter::new(BranchPtr::from(self.as_ref()));
if walker.try_forward(txn, index) {
walker.delete(txn, len)
} else {
panic!("Index {} is outside of the range of an array", index);
}
}
fn get<T: ReadTxn>(&self, _txn: &T, index: u32) -> Option<XmlNode> {
let branch = self.as_ref();
let (content, _) = branch.get_at(index)?;
if let ItemContent::Type(inner) = content {
let ptr: BranchPtr = inner.into();
XmlNode::try_from(ptr).ok()
} else {
None
}
}
fn successors<'a, T: ReadTxn>(&'a self, txn: &'a T) -> TreeWalker<'a, &'a T, T> {
TreeWalker::new(self.as_ref(), txn)
}
}
pub struct Attributes<'a, B, T>(Entries<'a, B, T>);
impl<'a, B, T> Attributes<'a, B, T>
where
B: Borrow<T>,
T: ReadTxn,
{
pub fn new(branch: &'a Branch, txn: B) -> Self {
let entries = Entries::new(&branch.map, txn);
Attributes(entries)
}
}
impl<'a, B, T> Iterator for Attributes<'a, B, T>
where
B: Borrow<T>,
T: ReadTxn,
{
type Item = (&'a str, String);
fn next(&mut self) -> Option<Self::Item> {
let (key, block) = self.0.next()?;
let txn = self.0.txn.borrow();
let value = block
.content
.get_last()
.map(|v| v.to_string(txn))
.unwrap_or(String::default());
Some((key.as_ref(), value))
}
}
pub struct TreeWalker<'a, B, T> {
current: Option<&'a Item>,
root: TypePtr,
first_call: bool,
_txn: B,
_marker: PhantomData<T>,
}
impl<'a, B, T: ReadTxn> TreeWalker<'a, B, T>
where
B: Borrow<T>,
T: ReadTxn,
{
pub fn new(root: &'a Branch, txn: B) -> Self {
TreeWalker {
current: root.start.as_deref(),
root: TypePtr::Branch(BranchPtr::from(root)),
first_call: true,
_txn: txn,
_marker: PhantomData::default(),
}
}
}
impl<'a, B, T: ReadTxn> Iterator for TreeWalker<'a, B, T>
where
B: Borrow<T>,
T: ReadTxn,
{
type Item = XmlNode;
fn next(&mut self) -> Option<Self::Item> {
fn try_descend(item: &Item) -> Option<&Item> {
if let ItemContent::Type(t) = &item.content {
let inner = t.as_ref();
match inner.type_ref() {
TypeRef::XmlElement(_) | TypeRef::XmlFragment if !item.is_deleted() => {
return inner.start.as_deref();
}
_ => { }
}
}
None
}
let mut result = None;
let mut n = self.current.take();
if let Some(current) = n {
if !self.first_call || current.is_deleted() {
while {
if let Some(current) = n {
if let Some(ptr) = try_descend(current) {
n = Some(ptr);
} else {
while let Some(current) = n {
if let Some(right) = current.right.as_ref() {
n = Some(right);
break;
} else if current.parent == self.root {
n = None;
} else {
let ptr = current.parent.as_branch().unwrap();
n = ptr.item.as_deref();
}
}
}
}
if let Some(current) = n {
current.is_deleted()
} else {
false
}
} {}
}
self.first_call = false;
self.current = n;
}
if let Some(current) = self.current {
if let ItemContent::Type(t) = ¤t.content {
result = XmlNode::try_from(BranchPtr::from(t)).ok();
}
}
result
}
}
pub struct XmlTextEvent {
pub(crate) current_target: BranchPtr,
target: XmlTextRef,
delta: UnsafeCell<Option<Vec<Delta>>>,
keys: UnsafeCell<Result<HashMap<Arc<str>, EntryChange>, HashSet<Option<Arc<str>>>>>,
}
impl XmlTextEvent {
pub(crate) fn new(branch_ref: BranchPtr, key_changes: HashSet<Option<Arc<str>>>) -> Self {
let current_target = branch_ref.clone();
let target = XmlTextRef::from(branch_ref);
XmlTextEvent {
target,
current_target,
delta: UnsafeCell::new(None),
keys: UnsafeCell::new(Err(key_changes)),
}
}
pub fn target(&self) -> &XmlTextRef {
&self.target
}
pub fn path(&self) -> Path {
Branch::path(self.current_target, self.target.0)
}
pub fn delta(&self, txn: &TransactionMut) -> &[Delta] {
let delta = unsafe { self.delta.get().as_mut().unwrap() };
delta
.get_or_insert_with(|| TextEvent::get_delta(self.target.0, txn))
.as_slice()
}
pub fn keys(&self, txn: &TransactionMut) -> &HashMap<Arc<str>, EntryChange> {
let keys = unsafe { self.keys.get().as_mut().unwrap() };
match keys {
Ok(keys) => {
return keys;
}
Err(subs) => {
let subs = event_keys(txn, self.target.0, subs);
*keys = Ok(subs);
if let Ok(keys) = keys {
keys
} else {
panic!("Defect: should not happen");
}
}
}
}
}
pub struct Siblings<'a, T> {
current: Option<ItemPtr>,
_txn: &'a T,
}
impl<'a, T> Siblings<'a, T> {
fn new(current: Option<ItemPtr>, txn: &'a T) -> Self {
Siblings { current, _txn: txn }
}
}
impl<'a, T> Iterator for Siblings<'a, T> {
type Item = XmlNode;
fn next(&mut self) -> Option<Self::Item> {
while let Some(item) = self.current.as_deref() {
self.current = item.right;
if let Some(right) = self.current.as_deref() {
if !right.is_deleted() {
if let ItemContent::Type(inner) = &right.content {
let ptr = BranchPtr::from(inner);
return XmlNode::try_from(ptr).ok();
}
}
}
}
None
}
}
impl<'a, T> DoubleEndedIterator for Siblings<'a, T> {
fn next_back(&mut self) -> Option<Self::Item> {
while let Some(item) = self.current.as_deref() {
self.current = item.left;
if let Some(left) = self.current.as_deref() {
if !left.is_deleted() {
if let ItemContent::Type(inner) = &left.content {
let ptr = BranchPtr::from(inner);
return XmlNode::try_from(ptr).ok();
}
}
}
}
None
}
}
pub struct XmlEvent {
pub(crate) current_target: BranchPtr,
target: XmlNode,
change_set: UnsafeCell<Option<Box<ChangeSet<Change>>>>,
keys: UnsafeCell<Result<HashMap<Arc<str>, EntryChange>, HashSet<Option<Arc<str>>>>>,
children_changed: bool,
}
impl XmlEvent {
pub(crate) fn new(branch_ref: BranchPtr, key_changes: HashSet<Option<Arc<str>>>) -> Self {
let current_target = branch_ref.clone();
let children_changed = key_changes.iter().any(Option::is_none);
XmlEvent {
target: XmlNode::try_from(branch_ref).unwrap(),
current_target,
change_set: UnsafeCell::new(None),
keys: UnsafeCell::new(Err(key_changes)),
children_changed,
}
}
pub fn children_changed(&self) -> bool {
self.children_changed
}
pub fn target(&self) -> &XmlNode {
&self.target
}
pub fn path(&self) -> Path {
Branch::path(self.current_target, self.target.as_ptr())
}
pub fn delta(&self, txn: &TransactionMut) -> &[Change] {
self.changes(txn).delta.as_slice()
}
pub fn added(&self, txn: &TransactionMut) -> &HashSet<ID> {
&self.changes(txn).added
}
pub fn deleted(&self, txn: &TransactionMut) -> &HashSet<ID> {
&self.changes(txn).deleted
}
pub fn keys(&self, txn: &TransactionMut) -> &HashMap<Arc<str>, EntryChange> {
let keys = unsafe { self.keys.get().as_mut().unwrap() };
match keys {
Ok(keys) => keys,
Err(subs) => {
let subs = event_keys(txn, self.target.as_ptr(), subs);
*keys = Ok(subs);
if let Ok(keys) = keys {
keys
} else {
panic!("Defect: should not happen");
}
}
}
}
fn changes(&self, txn: &TransactionMut) -> &ChangeSet<Change> {
let change_set = unsafe { self.change_set.get().as_mut().unwrap() };
change_set
.get_or_insert_with(|| Box::new(event_change_set(txn, self.target.as_ptr().start)))
}
}
#[cfg(test)]
mod test {
use crate::branch::BranchPtr;
use crate::test_utils::exchange_updates;
use crate::transaction::ReadTxn;
use crate::types::xml::{Xml, XmlFragment, XmlNode};
use crate::types::{Attrs, Change, EntryChange, Value};
use crate::updates::decoder::Decode;
use crate::updates::encoder::{Encoder, EncoderV1};
use crate::{
Any, Doc, GetString, Observable, SharedRef, StateVector, Text, Transact, Update,
XmlElementPrelim, XmlTextPrelim, XmlTextRef,
};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
#[test]
fn insert_attribute() {
let d1 = Doc::with_client_id(1);
let f = d1.get_or_insert_xml_fragment("xml");
let mut t1 = d1.transact_mut();
let xml1 = f.push_back(&mut t1, XmlElementPrelim::empty("div"));
xml1.insert_attribute(&mut t1, "height", 10.to_string());
assert_eq!(xml1.get_attribute(&t1, "height"), Some("10".to_string()));
let d2 = Doc::with_client_id(1);
let f = d2.get_or_insert_xml_fragment("xml");
let mut t2 = d2.transact_mut();
let xml2 = f.push_back(&mut t2, XmlElementPrelim::empty("div"));
let u = t1.encode_state_as_update_v1(&StateVector::default());
t2.apply_update(Update::decode_v1(u.as_slice()).unwrap());
assert_eq!(xml2.get_attribute(&t2, "height"), Some("10".to_string()));
}
#[test]
fn tree_walker() {
let doc = Doc::with_client_id(1);
let root = doc.get_or_insert_xml_fragment("xml");
let mut txn = doc.transact_mut();
let p1 = root.push_back(&mut txn, XmlElementPrelim::empty("p"));
p1.push_back(&mut txn, XmlTextPrelim::new(""));
p1.push_back(&mut txn, XmlTextPrelim::new(""));
let p2 = root.push_back(&mut txn, XmlElementPrelim::empty("p"));
root.push_back(&mut txn, XmlElementPrelim::empty("img"));
let all_paragraphs = root.successors(&txn).filter_map(|n| match n {
XmlNode::Element(e) if e.tag() == &"p".into() => Some(e),
_ => None,
});
let actual: Vec<_> = all_paragraphs.collect();
assert_eq!(
actual.len(),
2,
"query selector should found two paragraphs"
);
assert_eq!(
actual[0].hook(),
p1.hook(),
"query selector found 1st paragraph"
);
assert_eq!(
actual[1].hook(),
p2.hook(),
"query selector found 2nd paragraph"
);
}
#[test]
fn text_attributes() {
let doc = Doc::with_client_id(1);
let f = doc.get_or_insert_xml_fragment("test");
let mut txn = doc.transact_mut();
let txt = f.push_back(&mut txn, XmlTextPrelim::new(""));
txt.insert_attribute(&mut txn, "test", 42.to_string());
assert_eq!(txt.get_attribute(&txn, "test"), Some("42".to_string()));
let actual: Vec<_> = txt.attributes(&txn).collect();
assert_eq!(actual, vec![("test", "42".to_string())]);
}
#[test]
fn siblings() {
let doc = Doc::with_client_id(1);
let root = doc.get_or_insert_xml_fragment("root");
let mut txn = doc.transact_mut();
let first = root.push_back(&mut txn, XmlTextPrelim::new("hello"));
let second = root.push_back(&mut txn, XmlElementPrelim::empty("p"));
assert_eq!(
&first.siblings(&txn).next().unwrap().id(),
second.hook().id(),
"first.next_sibling should point to second"
);
assert_eq!(
&second.siblings(&txn).next_back().unwrap().id(),
first.hook().id(),
"second.prev_sibling should point to first"
);
assert_eq!(
&first.parent().unwrap().id(),
root.hook().id(),
"first.parent should point to root"
);
assert!(root.parent().is_none(), "root parent should not exist");
assert_eq!(
&root.first_child().unwrap().id(),
first.hook().id(),
"root.first_child should point to first"
);
}
#[test]
fn serialization() {
let d1 = Doc::with_client_id(1);
let r1 = d1.get_or_insert_xml_fragment("root");
let mut t1 = d1.transact_mut();
let _first = r1.push_back(&mut t1, XmlTextPrelim::new("hello"));
r1.push_back(&mut t1, XmlElementPrelim::empty("p"));
let expected = "hello<p></p>";
assert_eq!(r1.get_string(&t1), expected);
let u1 = t1.encode_state_as_update_v1(&StateVector::default());
let d2 = Doc::with_client_id(2);
let r2 = d2.get_or_insert_xml_fragment("root");
let mut t2 = d2.transact_mut();
t2.apply_update(Update::decode_v1(u1.as_slice()).unwrap());
assert_eq!(r2.get_string(&t2), expected);
}
#[test]
fn serialization_compatibility() {
let d1 = Doc::with_client_id(1);
let r1 = d1.get_or_insert_xml_fragment("root");
let mut t1 = d1.transact_mut();
let _first = r1.push_back(&mut t1, XmlTextPrelim::new("hello"));
r1.push_back(&mut t1, XmlElementPrelim::empty("p"));
let expected = &[
1, 3, 1, 0, 7, 1, 4, 114, 111, 111, 116, 6, 4, 0, 1, 0, 5, 104, 101, 108, 108, 111,
135, 1, 0, 3, 1, 112, 0,
];
let u1 = t1.encode_state_as_update_v1(&StateVector::default());
assert_eq!(u1.as_slice(), expected);
}
#[test]
fn event_observers() {
let d1 = Doc::with_client_id(1);
let f = d1.get_or_insert_xml_fragment("xml");
let xml = f.insert(&mut d1.transact_mut(), 0, XmlElementPrelim::empty("test"));
let d2 = Doc::with_client_id(2);
let f = d2.get_or_insert_xml_fragment("xml");
exchange_updates(&[&d1, &d2]);
let xml2 = f
.get(&d2.transact(), 0)
.unwrap()
.into_xml_element()
.unwrap();
let attributes = Rc::new(RefCell::new(None));
let nodes = Rc::new(RefCell::new(None));
let attributes_c = attributes.clone();
let nodes_c = nodes.clone();
let _sub = xml.observe(move |txn, e| {
*attributes_c.borrow_mut() = Some(e.keys(txn).clone());
*nodes_c.borrow_mut() = Some(e.delta(txn).to_vec());
});
{
let mut txn = d1.transact_mut();
xml.insert_attribute(&mut txn, "key1", "value1");
xml.insert_attribute(&mut txn, "key2", "value2");
}
assert!(nodes.borrow_mut().take().unwrap().is_empty());
assert_eq!(
attributes.borrow_mut().take(),
Some(HashMap::from([
(
"key1".into(),
EntryChange::Inserted(Any::String("value1".into()).into())
),
(
"key2".into(),
EntryChange::Inserted(Any::String("value2".into()).into())
)
]))
);
{
let mut txn = d1.transact_mut();
xml.insert_attribute(&mut txn, "key1", "value11");
xml.remove_attribute(&mut txn, &"key2");
}
assert!(nodes.borrow_mut().take().unwrap().is_empty());
assert_eq!(
attributes.borrow_mut().take(),
Some(HashMap::from([
(
"key1".into(),
EntryChange::Updated(
Any::String("value1".into()).into(),
Any::String("value11".into()).into()
)
),
(
"key2".into(),
EntryChange::Removed(Any::String("value2".into()).into())
)
]))
);
let (nested_txt, nested_xml) = {
let mut txn = d1.transact_mut();
let txt = xml.insert(&mut txn, 0, XmlTextPrelim::new(""));
let xml2 = xml.insert(&mut txn, 1, XmlElementPrelim::empty("div"));
(txt, xml2)
};
assert_eq!(
nodes.borrow_mut().take(),
Some(vec![Change::Added(vec![
Value::YXmlText(nested_txt.clone()),
Value::YXmlElement(nested_xml.clone())
])])
);
assert_eq!(attributes.borrow_mut().take(), Some(HashMap::new()));
let nested_xml2 = {
let mut txn = d1.transact_mut();
xml.remove_range(&mut txn, 1, 1);
xml.insert(&mut txn, 1, XmlElementPrelim::empty("p"))
};
assert_eq!(
nodes.borrow_mut().take(),
Some(vec![
Change::Retain(1),
Change::Added(vec![Value::YXmlElement(nested_xml2.clone())]),
Change::Removed(1),
])
);
assert_eq!(attributes.borrow_mut().take(), Some(HashMap::new()));
let attributes = Rc::new(RefCell::new(None));
let nodes = Rc::new(RefCell::new(None));
let attributes_c = attributes.clone();
let nodes_c = nodes.clone();
let _sub = xml2.observe(move |txn, e| {
*attributes_c.borrow_mut() = Some(e.keys(txn).clone());
*nodes_c.borrow_mut() = Some(e.delta(txn).to_vec());
});
{
let t1 = d1.transact_mut();
let mut t2 = d2.transact_mut();
let sv = t2.state_vector();
let mut encoder = EncoderV1::new();
t1.encode_diff(&sv, &mut encoder);
t2.apply_update(Update::decode_v1(encoder.to_vec().as_slice()).unwrap());
}
assert_eq!(
nodes.borrow_mut().take(),
Some(vec![Change::Added(vec![
Value::YXmlText(nested_txt),
Value::YXmlElement(nested_xml2)
])])
);
assert_eq!(
attributes.borrow_mut().take(),
Some(HashMap::from([(
"key1".into(),
EntryChange::Inserted(Any::String("value11".into()).into())
)]))
);
}
#[test]
fn xml_to_string() {
let doc = Doc::new();
let f = doc.get_or_insert_xml_fragment("test");
let mut txn = doc.transact_mut();
let div = f.push_back(&mut txn, XmlElementPrelim::empty("div"));
div.insert_attribute(&mut txn, "class", "t-button");
let text = div.push_back(&mut txn, XmlTextPrelim::new("hello world"));
text.format(
&mut txn,
6,
5,
Attrs::from([(
"a".into(),
HashMap::from([("href".into(), "http://domain.org")]).into(),
)]),
);
drop(txn);
let str = f.get_string(&doc.transact());
assert_eq!(
str.as_str(),
"<div class=\"t-button\">hello <a href=\"http://domain.org\">world</a></div>"
)
}
#[test]
fn xml_to_string_2() {
let doc = Doc::new();
let f = doc.get_or_insert_xml_fragment("article");
let xml = f.insert(&mut doc.transact_mut(), 0, XmlTextPrelim::new(""));
let mut txn = doc.transact_mut();
let bold = Attrs::from([("b".into(), true.into())]);
let italic = Attrs::from([("i".into(), true.into())]);
xml.insert(&mut txn, 0, "hello ");
xml.insert_with_attributes(&mut txn, 6, "world", italic);
xml.format(&mut txn, 0, 5, bold);
assert_eq!(xml.get_string(&txn), "<b>hello</b> <i>world</i>");
let remove_italic = Attrs::from([("i".into(), Any::Null)]);
xml.format(&mut txn, 6, 5, remove_italic);
assert_eq!(xml.get_string(&txn), "<b>hello</b> world");
}
#[test]
fn format_attributes_decode_compatibility_v1() {
let data = &[
1, 6, 1, 0, 6, 1, 4, 116, 101, 115, 116, 1, 105, 4, 116, 114, 117, 101, 132, 1, 0, 6,
104, 101, 108, 108, 111, 32, 132, 1, 6, 5, 119, 111, 114, 108, 100, 134, 1, 11, 1, 105,
4, 110, 117, 108, 108, 198, 1, 6, 1, 7, 1, 98, 4, 116, 114, 117, 101, 134, 1, 12, 1,
98, 4, 110, 117, 108, 108, 0,
];
let update = Update::decode_v1(data).unwrap();
let doc = Doc::new();
let txt = doc.get_or_insert_text("test");
let txt = XmlTextRef::from(BranchPtr::from(txt.as_ref()));
let mut txn = doc.transact_mut();
txn.apply_update(update);
assert_eq!(txt.get_string(&txn), "<i>hello </i><b><i>world</i></b>");
let actual = txn.encode_state_as_update_v1(&StateVector::default());
assert_eq!(actual, data);
}
#[test]
fn format_attributes_decode_compatibility_v2() {
let data = &[
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,
134, 26, 19, 116, 101, 115, 116, 105, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108,
100, 105, 98, 98, 4, 1, 6, 5, 65, 1, 1, 1, 0, 0, 1, 6, 0, 120, 126, 120, 126, 0,
];
let update = Update::decode_v2(data).unwrap();
let doc = Doc::new();
let txt = doc.get_or_insert_text("test");
let txt = XmlTextRef::from(BranchPtr::from(txt.as_ref()));
let mut txn = doc.transact_mut();
txn.apply_update(update);
assert_eq!(txt.get_string(&txn), "<i>hello </i><b><i>world</i></b>");
let actual = txn.encode_state_as_update_v2(&StateVector::default());
assert_eq!(actual, data);
}
}