1use crate::block::{BlockCell, Item, ItemContent, ItemPosition, ItemPtr, Prelim};
2use crate::types::array::ArrayEvent;
3use crate::types::map::MapEvent;
4use crate::types::text::TextEvent;
5use crate::types::xml::{XmlEvent, XmlTextEvent};
6use crate::types::{
7 Entries, Event, Events, Path, PathSegment, RootRef, SharedRef, TypePtr, TypeRef,
8};
9use crate::{
10 ArrayRef, Doc, MapRef, Observer, Origin, Out, ReadTxn, Subscription, TextRef, TransactionMut,
11 WriteTxn, XmlElementRef, XmlFragmentRef, XmlTextRef, ID,
12};
13use serde::{Deserialize, Serialize};
14use std::borrow::Borrow;
15use std::collections::{HashMap, HashSet, VecDeque};
16use std::convert::TryFrom;
17use std::fmt::Formatter;
18use std::hash::{Hash, Hasher};
19use std::marker::PhantomData;
20use std::ops::{Deref, DerefMut};
21use std::ptr::NonNull;
22use std::sync::Arc;
23
24#[repr(transparent)]
27#[derive(Clone, Copy, Hash)]
28pub struct BranchPtr(NonNull<Branch>);
29
30unsafe impl Send for BranchPtr {}
31unsafe impl Sync for BranchPtr {}
32
33impl 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
49impl 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
61impl Into<TypePtr> for BranchPtr {
62 fn into(self) -> TypePtr {
63 TypePtr::Branch(self)
64 }
65}
66
67impl 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
75impl AsRef<Branch> for BranchPtr {
76 fn as_ref(&self) -> &Branch {
77 self.deref()
78 }
79}
80
81impl AsMut<Branch> for BranchPtr {
82 fn as_mut(&mut self) -> &mut Branch {
83 self.deref_mut()
84 }
85}
86
87impl Deref for BranchPtr {
88 type Target = Branch;
89
90 fn deref(&self) -> &Self::Target {
91 unsafe { self.0.as_ref() }
92 }
93}
94
95impl DerefMut for BranchPtr {
96 fn deref_mut(&mut self) -> &mut Self::Target {
97 unsafe { self.0.as_mut() }
98 }
99}
100
101impl<'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
108impl<'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
117impl<'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
124impl Into<Out> for BranchPtr {
125 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 #[cfg(feature = "weak")]
138 TypeRef::WeakLink(_) => Out::YWeakLink(crate::WeakRef::from(self)),
139 _ => Out::UndefinedRef(self),
140 }
141 }
142}
143
144impl Eq for BranchPtr {}
145
146#[cfg(not(test))]
147impl 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)]
154impl 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
166impl 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
172pub struct Branch {
174 pub(crate) start: Option<ItemPtr>,
185
186 pub(crate) map: HashMap<Arc<str>, ItemPtr>,
193
194 pub(crate) item: Option<ItemPtr>,
199
200 pub(crate) name: Option<Arc<str>>,
202
203 pub block_len: u32,
206
207 pub content_len: u32,
208
209 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")]
218type ObserveFn = Box<dyn Fn(&TransactionMut, &Event) + Send + Sync + 'static>;
219#[cfg(feature = "sync")]
220type DeepObserveFn = Box<dyn Fn(&TransactionMut, &Events) + Send + Sync + 'static>;
221
222#[cfg(not(feature = "sync"))]
223type ObserveFn = Box<dyn Fn(&TransactionMut, &Event) + 'static>;
224#[cfg(not(feature = "sync"))]
225type DeepObserveFn = Box<dyn Fn(&TransactionMut, &Events) + 'static>;
226
227impl 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
233impl Eq for Branch {}
234
235impl 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
245impl 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 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 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 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 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 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 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 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 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 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 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 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 path.push_front(PathSegment::Key(parent_sub));
518 } else {
519 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
639pub(crate) struct Iter<'a, T> {
640 ptr: Option<&'a ItemPtr>,
641 _txn: &'a T,
642}
643
644impl<'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
650impl<'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#[repr(transparent)]
684#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
685pub struct Root<S> {
686 pub name: Arc<str>,
688 _tag: PhantomData<S>,
689}
690
691impl<S: RootRef> Root<S> {
692 pub fn new<N: Into<Arc<str>>>(name: N) -> Self {
696 Root {
697 name: name.into(),
698 _tag: PhantomData::default(),
699 }
700 }
701
702 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
711impl<S: SharedRef> Root<S> {
712 pub fn get<T: ReadTxn>(&self, txn: &T) -> Option<S> {
715 txn.store().get_type(self.name.clone()).map(S::from)
716 }
717}
718
719impl<S> Into<BranchID> for Root<S> {
720 fn into(self) -> BranchID {
721 BranchID::Root(self.name)
722 }
723}
724
725#[repr(transparent)]
755#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
756pub struct Nested<S> {
757 pub id: ID,
758 _tag: PhantomData<S>,
759}
760
761impl<S> Nested<S> {
762 pub fn new(id: ID) -> Self {
763 Nested {
764 id,
765 _tag: PhantomData::default(),
766 }
767 }
768}
769
770impl<S: SharedRef> Nested<S> {
771 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
791impl<S> Into<BranchID> for Nested<S> {
792 fn into(self) -> BranchID {
793 BranchID::Nested(self.id)
794 }
795}
796
797#[derive(Clone, Serialize, Deserialize)]
801pub struct Hook<S> {
802 id: BranchID,
803 _tag: PhantomData<S>,
804}
805
806impl<S> Hook<S> {
807 #[inline]
809 pub fn id(&self) -> &BranchID {
810 &self.id
811 }
812}
813
814impl<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
820impl<S> Eq for Hook<S> {}
821
822impl<S> PartialEq for Hook<S> {
823 fn eq(&self, other: &Self) -> bool {
824 self.id == other.id
825 }
826}
827
828impl<S> Hash for Hook<S> {
829 fn hash<H: Hasher>(&self, state: &mut H) {
830 self.id.hash(state)
831 }
832}
833
834impl<S: SharedRef> Hook<S> {
835 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 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
879impl<S: RootRef> Hook<S> {
880 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
890impl<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
899impl<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
908impl<S> From<BranchID> for Hook<S> {
909 fn from(id: BranchID) -> Self {
910 Hook {
911 id,
912 _tag: PhantomData::default(),
913 }
914 }
915}
916
917impl<S> Into<BranchID> for Hook<S> {
918 fn into(self) -> BranchID {
919 self.id
920 }
921}
922
923#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
926pub enum BranchID {
927 Nested(ID),
928 Root(Arc<str>),
929}
930
931impl 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
955impl 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}