1use crate::transaction::{ImplicitTransaction, YTransaction};
2use crate::Result;
3use gloo_utils::format::JsValueSerdeExt;
4use std::ops::Deref;
5use wasm_bindgen::JsValue;
6use yrs::{BranchID, Doc, Hook, ReadTxn, SharedRef, Transact, Transaction, TransactionMut};
7
8pub enum SharedCollection<P, S> {
9 Integrated(Integrated<S>),
10 Prelim(P),
11}
12
13impl<P, S: SharedRef + 'static> SharedCollection<P, S> {
14 #[inline]
15 pub fn prelim(prelim: P) -> Self {
16 SharedCollection::Prelim(prelim)
17 }
18
19 #[inline]
20 pub fn integrated(shared_ref: S, doc: Doc) -> Self {
21 SharedCollection::Integrated(Integrated::new(shared_ref, doc))
22 }
23
24 pub fn id(&self) -> crate::Result<JsValue> {
25 match self {
26 SharedCollection::Prelim(_) => {
27 Err(JsValue::from_str(crate::js::errors::INVALID_PRELIM_OP))
28 }
29 SharedCollection::Integrated(c) => {
30 let branch_id = c.hook.id();
31 JsValue::from_serde(branch_id).map_err(|e| JsValue::from_str(&e.to_string()))
32 }
33 }
34 }
35
36 pub fn try_integrated(&self) -> Result<(&BranchID, &Doc)> {
37 match self {
38 SharedCollection::Integrated(i) => {
39 let branch_id = i.hook.id();
40 let doc = &i.doc;
41 Ok((branch_id, doc))
42 }
43 SharedCollection::Prelim(_) => {
44 Err(JsValue::from_str(crate::js::errors::INVALID_PRELIM_OP))
45 }
46 }
47 }
48
49 #[inline]
50 pub fn is_prelim(&self) -> bool {
51 match self {
52 SharedCollection::Prelim(_) => true,
53 SharedCollection::Integrated(_) => false,
54 }
55 }
56
57 pub fn is_alive(&self, txn: &YTransaction) -> bool {
58 match self {
59 SharedCollection::Prelim(_) => true,
60 SharedCollection::Integrated(col) => {
61 let desc = &col.hook;
62 desc.get(txn.deref()).is_some()
63 }
64 }
65 }
66
67 #[inline]
68 pub fn branch_id(&self) -> Option<&BranchID> {
69 match self {
70 SharedCollection::Prelim(_) => None,
71 SharedCollection::Integrated(v) => Some(v.hook.id()),
72 }
73 }
74}
75
76pub struct Integrated<S> {
77 pub hook: Hook<S>,
78 pub doc: Doc,
79}
80
81impl<S: SharedRef + 'static> Integrated<S> {
82 pub fn new(shared_ref: S, doc: Doc) -> Self {
83 let desc = shared_ref.hook();
84 Integrated { hook: desc, doc }
85 }
86
87 pub fn readonly<F, T>(&self, txn: &ImplicitTransaction, f: F) -> Result<T>
88 where
89 F: FnOnce(&S, &TransactionMut<'_>) -> Result<T>,
90 {
91 match YTransaction::from_implicit(txn)? {
92 Some(txn) => {
93 let txn: &TransactionMut = &*txn;
94 let shared_ref = self.resolve(txn)?;
95 f(&shared_ref, txn)
96 }
97 None => {
98 let txn = self.transact_mut()?;
99 let shared_ref = self.resolve(&txn)?;
100 f(&shared_ref, &txn)
101 }
102 }
103 }
104
105 pub fn mutably<F, T>(&self, mut txn: ImplicitTransaction, f: F) -> Result<T>
106 where
107 F: FnOnce(&S, &mut TransactionMut<'_>) -> Result<T>,
108 {
109 match YTransaction::from_implicit_mut(&mut txn)? {
110 Some(mut txn) => {
111 let txn = txn.as_mut()?;
112 let shared_ref = self.resolve(txn)?;
113 f(&shared_ref, txn)
114 }
115 None => {
116 let mut txn = self.transact_mut()?;
117 let shared_ref = self.resolve(&mut txn)?;
118 f(&shared_ref, &mut txn)
119 }
120 }
121 }
122
123 pub fn resolve<T: ReadTxn>(&self, txn: &T) -> Result<S> {
124 match self.hook.get(txn) {
125 Some(shared_ref) => Ok(shared_ref),
126 None => Err(JsValue::from_str(crate::js::errors::REF_DISPOSED)),
127 }
128 }
129
130 pub fn transact(&self) -> Result<Transaction> {
131 match self.doc.try_transact() {
132 Ok(tx) => Ok(tx),
133 Err(_) => Err(JsValue::from_str(crate::js::errors::ANOTHER_RW_TX)),
134 }
135 }
136
137 pub fn transact_mut(&self) -> Result<TransactionMut> {
138 match self.doc.try_transact_mut() {
139 Ok(tx) => Ok(tx),
140 Err(_) => Err(JsValue::from_str(crate::js::errors::ANOTHER_TX)),
141 }
142 }
143}