oficina / docs Public

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

Plain git — no account needed to clone.

Download

Download this file

1use crate::block::{ItemContent, ItemPtr};
2use crate::branch::{Branch, BranchPtr};
3use crate::types::{AsPrelim, ToJson};
4use crate::{
5 any, Any, ArrayRef, Doc, GetString, In, MapPrelim, MapRef, ReadTxn, TextRef, XmlElementRef,
6 XmlFragmentRef, XmlTextRef,
7};
8use std::convert::TryFrom;
9use std::fmt::Formatter;
10use std::sync::Arc;
11
12/// Value that can be returned by Yrs data types. This includes [Any] which is an extension
13/// representation of JSON, but also nested complex collaborative structures specific to Yrs.
14#[derive(Debug, Clone, PartialEq)]
15pub enum Out {
16 /// Any value that it treated as a single element in its entirety.
17 Any(Any),
18 /// Instance of a [TextRef].
19 YText(TextRef),
20 /// Instance of an [ArrayRef].
21 YArray(ArrayRef),
22 /// Instance of a [MapRef].
23 YMap(MapRef),
24 /// Instance of a [XmlElementRef].
25 YXmlElement(XmlElementRef),
26 /// Instance of a [XmlFragmentRef].
27 YXmlFragment(XmlFragmentRef),
28 /// Instance of a [XmlTextRef].
29 YXmlText(XmlTextRef),
30 /// Subdocument.
31 YDoc(Doc),
32 /// Instance of a [WeakRef] or unspecified type (requires manual casting).
33 #[cfg(feature = "weak")]
34 YWeakLink(crate::WeakRef<BranchPtr>),
35 /// Instance of a shared collection of undefined type. Usually happens when it refers to a root
36 /// type that has not been defined locally. Can also refer to a [WeakRef] if "weak" feature flag
37 /// was not set.
38 UndefinedRef(BranchPtr),
39}
40
41impl Default for Out {
42 fn default() -> Self {
43 Out::Any(Any::Undefined)
44 }
45}
46
47impl Out {
48 /// Attempts to convert current [Out] value directly onto a different type, as along as it
49 /// implements [TryFrom] trait. If conversion is not possible, the original value is returned.
50 #[inline]
51 pub fn cast<T>(self) -> Result<T, Self>
52 where
53 T: TryFrom<Self, Error = Self>,
54 {
55 T::try_from(self)
56 }
57
58 /// Converts current value into stringified representation.
59 pub fn to_string<T: ReadTxn>(self, txn: &T) -> String {
60 match self {
61 Out::Any(a) => a.to_string(),
62 Out::YText(v) => v.get_string(txn),
63 Out::YArray(v) => v.to_json(txn).to_string(),
64 Out::YMap(v) => v.to_json(txn).to_string(),
65 Out::YXmlElement(v) => v.get_string(txn),
66 Out::YXmlFragment(v) => v.get_string(txn),
67 Out::YXmlText(v) => v.get_string(txn),
68 Out::YDoc(v) => v.to_string(),
69 #[cfg(feature = "weak")]
70 Out::YWeakLink(v) => {
71 let text_ref: crate::WeakRef<TextRef> = crate::WeakRef::from(v);
72 text_ref.get_string(txn)
73 }
74 Out::UndefinedRef(_) => "".to_string(),
75 }
76 }
77
78 pub fn try_branch(&self) -> Option<&Branch> {
79 match self {
80 Out::YText(b) => Some(b.as_ref()),
81 Out::YArray(b) => Some(b.as_ref()),
82 Out::YMap(b) => Some(b.as_ref()),
83 Out::YXmlElement(b) => Some(b.as_ref()),
84 Out::YXmlFragment(b) => Some(b.as_ref()),
85 Out::YXmlText(b) => Some(b.as_ref()),
86 #[cfg(feature = "weak")]
87 Out::YWeakLink(b) => Some(b.as_ref()),
88 Out::UndefinedRef(b) => Some(b.as_ref()),
89 Out::YDoc(_) => None,
90 Out::Any(_) => None,
91 }
92 }
93}
94
95impl TryFrom<ItemPtr> for Out {
96 type Error = ItemPtr;
97
98 fn try_from(value: ItemPtr) -> Result<Self, Self::Error> {
99 match value.content.get_last() {
100 None => Err(value),
101 Some(v) => Ok(v),
102 }
103 }
104}
105
106impl AsPrelim for Out {
107 type Prelim = In;
108
109 fn as_prelim<T: ReadTxn>(&self, txn: &T) -> Self::Prelim {
110 match self {
111 Out::Any(any) => In::Any(any.clone()),
112 Out::YText(v) => In::Text(v.as_prelim(txn)),
113 Out::YArray(v) => In::Array(v.as_prelim(txn)),
114 Out::YMap(v) => In::Map(v.as_prelim(txn)),
115 Out::YXmlElement(v) => In::XmlElement(v.as_prelim(txn)),
116 Out::YXmlFragment(v) => In::XmlFragment(v.as_prelim(txn)),
117 Out::YXmlText(v) => In::XmlText(v.as_prelim(txn)),
118 Out::YDoc(v) => In::Doc(v.clone()),
119 #[cfg(feature = "weak")]
120 Out::YWeakLink(v) => In::WeakLink(v.as_prelim(txn)),
121 Out::UndefinedRef(v) => infer_type_from_content(*v, txn),
122 }
123 }
124}
125
126fn infer_type_from_content<T: ReadTxn>(branch: BranchPtr, txn: &T) -> In {
127 let has_map = !branch.map.is_empty();
128 let mut ptr = branch.start;
129 let has_list = ptr.is_some();
130 let mut possible_text = false;
131 while let Some(curr) = ptr {
132 if !curr.is_deleted() {
133 possible_text = match &curr.content {
134 ItemContent::Embed(_) | ItemContent::Format(_, _) | ItemContent::String(_) => true,
135 _ => false,
136 };
137 break;
138 }
139 ptr = curr.right;
140 }
141
142 match (has_map, has_list, possible_text) {
143 (true, false, false) => In::Map(MapRef::from(branch).as_prelim(txn)),
144 (false, true, false) => In::Array(ArrayRef::from(branch).as_prelim(txn)),
145 (false, _, true) => In::Text(TextRef::from(branch).as_prelim(txn)),
146 (true, _, true) => In::XmlText(XmlTextRef::from(branch).as_prelim(txn)),
147 (true, true, false) => In::XmlElement(XmlElementRef::from(branch).as_prelim(txn)),
148 _ => In::Map(MapPrelim::default()), // if we have no content, default to map
149 }
150}
151
152impl<T> From<T> for Out
153where
154 T: Into<Any>,
155{
156 fn from(v: T) -> Self {
157 let any: Any = v.into();
158 Out::Any(any)
159 }
160}
161
162//FIXME: what we would like to have is an automatic trait implementation of TryFrom<Value> for
163// any type that implements TryFrom<Any,Error=Any>, but this causes compiler error.
164macro_rules! impl_try_from {
165 ($t:ty) => {
166 impl TryFrom<Out> for $t {
167 type Error = Out;
168
169 fn try_from(value: Out) -> Result<Self, Self::Error> {
170 use std::convert::TryInto;
171 match value {
172 Out::Any(any) => any.try_into().map_err(Out::Any),
173 other => Err(other),
174 }
175 }
176 }
177 };
178}
179
180impl_try_from!(bool);
181impl_try_from!(f32);
182impl_try_from!(f64);
183impl_try_from!(i16);
184impl_try_from!(i32);
185impl_try_from!(u16);
186impl_try_from!(u32);
187impl_try_from!(i64);
188impl_try_from!(isize);
189impl_try_from!(String);
190impl_try_from!(Arc<str>);
191impl_try_from!(Vec<u8>);
192impl_try_from!(Arc<[u8]>);
193
194impl ToJson for Out {
195 /// Converts current value into [Any] object equivalent that resembles enhanced JSON payload.
196 /// Rules are:
197 ///
198 /// - Primitive types ([Out::Any]) are passed right away, as no transformation is needed.
199 /// - [Out::YArray] is converted into JSON-like array.
200 /// - [Out::YMap] is converted into JSON-like object map.
201 /// - [Out::YText], [Out::YXmlText] and [Out::YXmlElement] are converted into strings
202 /// (XML types are stringified XML representation).
203 fn to_json<T: ReadTxn>(&self, txn: &T) -> Any {
204 match self {
205 Out::Any(a) => a.clone(),
206 Out::YText(v) => Any::from(v.get_string(txn)),
207 Out::YArray(v) => v.to_json(txn),
208 Out::YMap(v) => v.to_json(txn),
209 Out::YXmlElement(v) => Any::from(v.get_string(txn)),
210 Out::YXmlText(v) => Any::from(v.get_string(txn)),
211 Out::YXmlFragment(v) => Any::from(v.get_string(txn)),
212 Out::YDoc(doc) => any!({"guid": doc.guid().as_ref()}),
213 #[cfg(feature = "weak")]
214 Out::YWeakLink(_) => Any::Undefined,
215 Out::UndefinedRef(_) => Any::Undefined,
216 }
217 }
218}
219
220impl std::fmt::Display for Out {
221 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
222 match self {
223 Out::Any(v) => std::fmt::Display::fmt(v, f),
224 Out::YText(_) => write!(f, "TextRef"),
225 Out::YArray(_) => write!(f, "ArrayRef"),
226 Out::YMap(_) => write!(f, "MapRef"),
227 Out::YXmlElement(_) => write!(f, "XmlElementRef"),
228 Out::YXmlFragment(_) => write!(f, "XmlFragmentRef"),
229 Out::YXmlText(_) => write!(f, "XmlTextRef"),
230 #[cfg(feature = "weak")]
231 Out::YWeakLink(_) => write!(f, "WeakRef"),
232 Out::YDoc(v) => write!(f, "Doc(guid:{})", v.guid()),
233 Out::UndefinedRef(_) => write!(f, "UndefinedRef"),
234 }
235 }
236}
237