Skip to main content

yrs/
lib.rs

1use std::collections::{Bound, HashMap};
2use std::ffi::{c_char, c_void, CStr, CString};
3use std::mem::{forget, ManuallyDrop, MaybeUninit};
4use std::ops::{Deref, RangeBounds};
5use std::ptr::{null, null_mut};
6use std::sync::atomic::{AtomicPtr, Ordering};
7use std::sync::Arc;
8use yrs::block::{ClientID, EmbedPrelim, ItemContent, Prelim, Unused};
9use yrs::branch::BranchPtr;
10use yrs::encoding::read::Error;
11use yrs::error::UpdateError;
12use yrs::json_path::JsonPathIter as NativeJsonPathIter;
13use yrs::types::array::ArrayEvent;
14use yrs::types::array::ArrayIter as NativeArrayIter;
15use yrs::types::map::MapEvent;
16use yrs::types::map::MapIter as NativeMapIter;
17use yrs::types::text::{Diff, TextEvent, YChange};
18use yrs::types::weak::{LinkSource, Unquote as NativeUnquote, WeakEvent, WeakRef};
19use yrs::types::xml::{Attributes as NativeAttributes, XmlOut};
20use yrs::types::xml::{TreeWalker as NativeTreeWalker, XmlFragment};
21use yrs::types::xml::{XmlEvent, XmlTextEvent};
22use yrs::types::{Attrs, Change, Delta, EntryChange, Event, PathSegment, ToJson, TypeRef};
23use yrs::undo::EventKind;
24use yrs::updates::decoder::{Decode, DecoderV1};
25use yrs::updates::encoder::{Encode, Encoder, EncoderV1, EncoderV2};
26use yrs::{
27    uuid_v4, Any, Array, ArrayRef, Assoc, BranchID, GetString, IdSet, JsonPath, JsonPathEval, Map,
28    MapRef, Observable, OffsetKind, Options, Origin, Out, Quotable, ReadTxn, Snapshot, StateVector,
29    StickyIndex, Store, SubdocsEvent, SubdocsEventIter, Text, TextRef, Transact,
30    TransactionCleanupEvent, Update, Xml, XmlElementPrelim, XmlElementRef, XmlFragmentRef,
31    XmlTextPrelim, XmlTextRef, ID,
32};
33
34/// Flag used by `YInput` to pass JSON string for an object that should be deserialized and
35/// stored internally as fully fledged scalar type.
36pub const Y_JSON: i8 = -9;
37
38/// Flag used by `YInput` and `YOutput` to tag boolean values.
39pub const Y_JSON_BOOL: i8 = -8;
40
41/// Flag used by `YInput` and `YOutput` to tag floating point numbers.
42pub const Y_JSON_NUM: i8 = -7;
43
44/// Flag used by `YInput` and `YOutput` to tag 64-bit integer numbers.
45pub const Y_JSON_INT: i8 = -6;
46
47/// Flag used by `YInput` and `YOutput` to tag strings.
48pub const Y_JSON_STR: i8 = -5;
49
50/// Flag used by `YInput` and `YOutput` to tag binary content.
51pub const Y_JSON_BUF: i8 = -4;
52
53/// Flag used by `YInput` and `YOutput` to tag embedded JSON-like arrays of values,
54/// which themselves are `YInput` and `YOutput` instances respectively.
55pub const Y_JSON_ARR: i8 = -3;
56
57/// Flag used by `YInput` and `YOutput` to tag embedded JSON-like maps of key-value pairs,
58/// where keys are strings and v
59pub const Y_JSON_MAP: i8 = -2;
60
61/// Flag used by `YInput` and `YOutput` to tag JSON-like null values.
62pub const Y_JSON_NULL: i8 = -1;
63
64/// Flag used by `YInput` and `YOutput` to tag JSON-like undefined values.
65pub const Y_JSON_UNDEF: i8 = 0;
66
67/// Flag used by `YInput` and `YOutput` to tag content, which is an `YArray` shared type.
68pub const Y_ARRAY: i8 = 1;
69
70/// Flag used by `YInput` and `YOutput` to tag content, which is an `YMap` shared type.
71pub const Y_MAP: i8 = 2;
72
73/// Flag used by `YInput` and `YOutput` to tag content, which is an `YText` shared type.
74pub const Y_TEXT: i8 = 3;
75
76/// Flag used by `YInput` and `YOutput` to tag content, which is an `YXmlElement` shared type.
77pub const Y_XML_ELEM: i8 = 4;
78
79/// Flag used by `YInput` and `YOutput` to tag content, which is an `YXmlText` shared type.
80pub const Y_XML_TEXT: i8 = 5;
81
82/// Flag used by `YInput` and `YOutput` to tag content, which is an `YXmlFragment` shared type.
83pub const Y_XML_FRAG: i8 = 6;
84
85/// Flag used by `YInput` and `YOutput` to tag content, which is an `YDoc` shared type.
86pub const Y_DOC: i8 = 7;
87
88/// Flag used by `YInput` and `YOutput` to tag content, which is an `YWeakLink` shared type.
89pub const Y_WEAK_LINK: i8 = 8;
90
91/// Flag used by `YOutput` to tag content, which is an undefined shared type. This usually happens
92/// when it's referencing a root type that has not been initalized localy.
93pub const Y_UNDEFINED: i8 = 9;
94
95/// Flag used to mark a truthy boolean numbers.
96pub const Y_TRUE: u8 = 1;
97
98/// Flag used to mark a falsy boolean numbers.
99pub const Y_FALSE: u8 = 0;
100
101/// Flag used by `YOptions` to determine, that text operations offsets and length will be counted by
102/// the byte number of UTF8-encoded string.
103pub const Y_OFFSET_BYTES: u8 = 0;
104
105/// Flag used by `YOptions` to determine, that text operations offsets and length will be counted by
106/// UTF-16 chars of encoded string.
107pub const Y_OFFSET_UTF16: u8 = 1;
108
109/* pub types below are used by cbindgen for c header generation */
110
111/// A Yrs document type. Documents are the most important units of collaborative resources management.
112/// All shared collections live within a scope of their corresponding documents. All updates are
113/// generated on per-document basis (rather than individual shared type). All operations on shared
114/// collections happen via `YTransaction`, which lifetime is also bound to a document.
115///
116/// Document manages so-called root types, which are top-level shared types definitions (as opposed
117/// to recursively nested types).
118pub type Doc = yrs::Doc;
119
120/// A common shared data type. All Yrs instances can be refered to using this data type (use
121/// `ytype_kind` function if a specific type needs to be determined). Branch pointers are passed
122/// over type-specific functions like `ytext_insert`, `yarray_insert` or `ymap_insert` to perform
123/// a specific shared type operations.
124///
125/// Using write methods of different shared types (eg. `ytext_insert` and `yarray_insert`) over
126/// the same branch may result in undefined behavior.
127pub type Branch = yrs::branch::Branch;
128
129/// Subscription to any kind of observable events, like `ymap_observe`, `ydoc_observe_updates_v1` etc.
130/// This subscription can be destroyed by calling `yunobserve` function, which will cause to unsubscribe
131/// correlated callback.
132pub type Subscription = yrs::Subscription;
133
134/// Iterator structure used by shared array data type.
135#[repr(transparent)]
136pub struct ArrayIter(NativeArrayIter<&'static Transaction, Transaction>);
137
138/// Iterator structure used by `yweak_iter` function call.
139#[repr(transparent)]
140pub struct WeakIter(NativeUnquote<'static, Transaction>);
141
142/// Iterator structure used by shared map data type. Map iterators are unordered - there's no
143/// specific order in which map entries will be returned during consecutive iterator calls.
144#[repr(transparent)]
145pub struct MapIter(NativeMapIter<'static, &'static Transaction, Transaction>);
146
147/// Iterator structure used by XML nodes (elements and text) to iterate over node's attributes.
148/// Attribute iterators are unordered - there's no specific order in which map entries will be
149/// returned during consecutive iterator calls.
150#[repr(transparent)]
151pub struct Attributes(NativeAttributes<'static, &'static Transaction, Transaction>);
152
153/// Iterator used to traverse over the complex nested tree structure of a XML node. XML node
154/// iterator walks only over `YXmlElement` and `YXmlText` nodes. It does so in ordered manner (using
155/// the order in which children are ordered within their parent nodes) and using **depth-first**
156/// traverse.
157#[repr(transparent)]
158pub struct TreeWalker(NativeTreeWalker<'static, &'static Transaction, Transaction>);
159
160/// Transaction is one of the core types in Yrs. All operations that need to touch or
161/// modify a document's contents (a.k.a. block store), need to be executed in scope of a
162/// transaction.
163#[repr(transparent)]
164pub struct Transaction(TransactionInner);
165
166/// Iterator structure used by json path queries to traverse over the results of a query.
167#[repr(C)]
168pub struct JsonPathIter {
169    query: String,
170    json_path: Box<JsonPath<'static>>,
171    inner: NativeJsonPathIter<'static, Transaction>,
172}
173
174enum TransactionInner {
175    ReadOnly(yrs::Transaction<'static>),
176    ReadWrite(yrs::TransactionMut<'static>),
177}
178
179impl Transaction {
180    fn read_only(txn: yrs::Transaction) -> Self {
181        Transaction(TransactionInner::ReadOnly(unsafe {
182            std::mem::transmute(txn)
183        }))
184    }
185
186    fn read_write(txn: yrs::TransactionMut) -> Self {
187        Transaction(TransactionInner::ReadWrite(unsafe {
188            std::mem::transmute(txn)
189        }))
190    }
191
192    fn is_writeable(&self) -> bool {
193        match &self.0 {
194            TransactionInner::ReadOnly(_) => false,
195            TransactionInner::ReadWrite(_) => true,
196        }
197    }
198
199    fn as_mut(&mut self) -> Option<&mut yrs::TransactionMut<'static>> {
200        match &mut self.0 {
201            TransactionInner::ReadOnly(_) => None,
202            TransactionInner::ReadWrite(txn) => Some(txn),
203        }
204    }
205}
206
207impl ReadTxn for Transaction {
208    fn store(&self) -> &Store {
209        match &self.0 {
210            TransactionInner::ReadOnly(txn) => txn.store(),
211            TransactionInner::ReadWrite(txn) => txn.store(),
212        }
213    }
214}
215
216/// A structure representing single key-value entry of a map output (used by either
217/// embedded JSON-like maps or YMaps).
218#[repr(C)]
219pub struct YMapEntry {
220    /// Null-terminated string representing an entry's key component. Encoded as UTF-8.
221    pub key: *const c_char,
222    /// A `YOutput` value representing containing variadic content that can be stored withing map's
223    /// entry.
224    pub value: *const YOutput,
225}
226
227impl YMapEntry {
228    fn new(key: &str, value: Box<YOutput>) -> Self {
229        let key = CString::new(key).unwrap().into_raw();
230        let value = Box::into_raw(value) as *const YOutput;
231        YMapEntry { key, value }
232    }
233}
234
235impl Drop for YMapEntry {
236    fn drop(&mut self) {
237        unsafe {
238            drop(CString::from_raw(self.key as *mut c_char));
239            drop(Box::from_raw(self.value as *mut YOutput));
240        }
241    }
242}
243
244/// A structure representing single attribute of an either `YXmlElement` or `YXmlText` instance.
245/// It consists of attribute name and string, both of which are null-terminated UTF-8 strings.
246#[repr(C)]
247pub struct YXmlAttr {
248    pub name: *const c_char,
249    pub value: *const YOutput,
250}
251
252impl Drop for YXmlAttr {
253    fn drop(&mut self) {
254        unsafe {
255            drop(CString::from_raw(self.name as *mut _));
256            if (!self.value.is_null()) {
257                drop(Box::from_raw(self.value as *mut YOutput));
258            }
259        }
260    }
261}
262
263/// Configuration object used by `YDoc`.
264#[repr(C)]
265pub struct YOptions {
266    /// Globally unique 53-bit integer assigned to corresponding document replica as its identifier.
267    ///
268    /// If two clients share the same `id` and will perform any updates, it will result in
269    /// unrecoverable document state corruption. The same thing may happen if the client restored
270    /// document state from snapshot, that didn't contain all of that clients updates that were sent
271    /// to other peers.
272    pub id: u64,
273
274    /// A NULL-able globally unique Uuid v4 compatible null-terminated string identifier
275    /// of this document. If passed as NULL, a random Uuid will be generated instead.
276    pub guid: *const c_char,
277
278    /// A NULL-able, UTF-8 encoded, null-terminated string of a collection that this document
279    /// belongs to. It's used only by providers.
280    pub collection_id: *const c_char,
281
282    /// Encoding used by text editing operations on this document. It's used to compute
283    /// `YText`/`YXmlText` insertion offsets and text lengths. Either:
284    ///
285    /// - `Y_OFFSET_BYTES`
286    /// - `Y_OFFSET_UTF16`
287    pub encoding: u8,
288
289    /// Boolean flag used to determine if deleted blocks should be garbage collected or not
290    /// during the transaction commits. Setting this value to 0 means GC will be performed.
291    pub skip_gc: u8,
292
293    /// Boolean flag used to determine if subdocument should be loaded automatically.
294    /// If this is a subdocument, remote peers will load the document as well automatically.
295    pub auto_load: u8,
296
297    /// Boolean flag used to determine whether the document should be synced by the provider now.
298    pub should_load: u8,
299}
300
301impl Into<Options> for YOptions {
302    fn into(self) -> Options {
303        let encoding = match self.encoding {
304            Y_OFFSET_BYTES => OffsetKind::Bytes,
305            Y_OFFSET_UTF16 => OffsetKind::Utf16,
306            _ => panic!("Unrecognized YOptions.encoding type"),
307        };
308        let guid = if self.guid.is_null() {
309            uuid_v4()
310        } else {
311            let c_str = unsafe { CStr::from_ptr(self.guid) };
312            let str = c_str.to_str().unwrap();
313            str.into()
314        };
315        let collection_id = if self.collection_id.is_null() {
316            None
317        } else {
318            let c_str = unsafe { CStr::from_ptr(self.collection_id) };
319            let str = Arc::from(c_str.to_str().unwrap());
320            Some(str)
321        };
322        Options {
323            client_id: ClientID::new(self.id),
324            guid,
325            collection_id,
326            skip_gc: if self.skip_gc == 0 { false } else { true },
327            auto_load: if self.auto_load == 0 { false } else { true },
328            should_load: if self.should_load == 0 { false } else { true },
329            offset_kind: encoding,
330        }
331    }
332}
333
334impl From<Options> for YOptions {
335    fn from(o: Options) -> Self {
336        YOptions {
337            id: o.client_id.get(),
338            guid: CString::new(o.guid.as_ref()).unwrap().into_raw(),
339            collection_id: if let Some(collection_id) = o.collection_id {
340                CString::new(collection_id.to_string()).unwrap().into_raw()
341            } else {
342                null_mut()
343            },
344            encoding: match o.offset_kind {
345                OffsetKind::Bytes => Y_OFFSET_BYTES,
346                OffsetKind::Utf16 => Y_OFFSET_UTF16,
347            },
348            skip_gc: if o.skip_gc { 1 } else { 0 },
349            auto_load: if o.auto_load { 1 } else { 0 },
350            should_load: if o.should_load { 1 } else { 0 },
351        }
352    }
353}
354
355/// Returns default ceonfiguration for `YOptions`.
356#[no_mangle]
357pub unsafe extern "C" fn yoptions() -> YOptions {
358    Options::default().into()
359}
360
361/// Releases all memory-allocated resources bound to given document.
362#[no_mangle]
363pub unsafe extern "C" fn ydoc_destroy(value: *mut Doc) {
364    if !value.is_null() {
365        drop(Box::from_raw(value));
366    }
367}
368
369/// Frees all memory-allocated resources bound to a given [YMapEntry].
370#[no_mangle]
371pub unsafe extern "C" fn ymap_entry_destroy(value: *mut YMapEntry) {
372    if !value.is_null() {
373        drop(Box::from_raw(value));
374    }
375}
376
377/// Frees all memory-allocated resources bound to a given [YXmlAttr].
378#[no_mangle]
379pub unsafe extern "C" fn yxmlattr_destroy(attr: *mut YXmlAttr) {
380    if !attr.is_null() {
381        drop(Box::from_raw(attr));
382    }
383}
384
385/// Frees all memory-allocated resources bound to a given UTF-8 null-terminated string returned from
386/// Yrs document API. Yrs strings don't use libc malloc, so calling `free()` on them will fault.
387#[no_mangle]
388pub unsafe extern "C" fn ystring_destroy(str: *mut c_char) {
389    if !str.is_null() {
390        drop(CString::from_raw(str));
391    }
392}
393
394/// Frees all memory-allocated resources bound to a given binary returned from Yrs document API.
395/// Unlike strings binaries are not null-terminated and can contain null characters inside,
396/// therefore a size of memory to be released must be explicitly provided.
397/// Yrs binaries don't use libc malloc, so calling `free()` on them will fault.
398#[no_mangle]
399pub unsafe extern "C" fn ybinary_destroy(ptr: *mut c_char, len: u32) {
400    if !ptr.is_null() {
401        drop(Vec::from_raw_parts(ptr, len as usize, len as usize));
402    }
403}
404
405/// Creates a new [Doc] instance with a randomized unique client identifier.
406///
407/// Use [ydoc_destroy] in order to release created [Doc] resources.
408#[no_mangle]
409pub extern "C" fn ydoc_new() -> *mut Doc {
410    Box::into_raw(Box::new(Doc::new()))
411}
412
413/// Creates a shallow clone of a provided `doc` - it's realized by increasing the ref-count
414/// value of the document. In result both input and output documents point to the same instance.
415///
416/// Documents created this way can be destroyed via [ydoc_destroy] - keep in mind, that the memory
417/// will still be persisted until all strong references are dropped.
418#[no_mangle]
419pub unsafe extern "C" fn ydoc_clone(doc: *mut Doc) -> *mut Doc {
420    let doc = doc.as_mut().unwrap();
421    Box::into_raw(Box::new(doc.clone()))
422}
423
424/// Creates a new [Doc] instance with a specified `options`.
425///
426/// Use [ydoc_destroy] in order to release created [Doc] resources.
427#[no_mangle]
428pub extern "C" fn ydoc_new_with_options(options: YOptions) -> *mut Doc {
429    Box::into_raw(Box::new(Doc::with_options(options.into())))
430}
431
432/// Returns a unique client identifier of this [Doc] instance.
433#[no_mangle]
434pub unsafe extern "C" fn ydoc_id(doc: *mut Doc) -> u64 {
435    let doc = doc.as_ref().unwrap();
436    doc.client_id().get()
437}
438
439/// Returns a unique document identifier of this [Doc] instance.
440///
441/// Generated string resources should be released using [ystring_destroy] function.
442#[no_mangle]
443pub unsafe extern "C" fn ydoc_guid(doc: *mut Doc) -> *mut c_char {
444    let doc = doc.as_ref().unwrap();
445    let uid = doc.guid();
446    CString::new(uid.as_ref()).unwrap().into_raw()
447}
448
449/// Returns a collection identifier of this [Doc] instance.
450/// If none was defined, a `NULL` will be returned.
451///
452/// Generated string resources should be released using [ystring_destroy] function.
453#[no_mangle]
454pub unsafe extern "C" fn ydoc_collection_id(doc: *mut Doc) -> *mut c_char {
455    let doc = doc.as_ref().unwrap();
456    if let Some(cid) = doc.collection_id() {
457        CString::new(cid.as_ref()).unwrap().into_raw()
458    } else {
459        null_mut()
460    }
461}
462
463/// Returns status of should_load flag of this [Doc] instance, informing parent [Doc] if this
464/// document instance requested a data load.
465#[no_mangle]
466pub unsafe extern "C" fn ydoc_should_load(doc: *mut Doc) -> u8 {
467    let doc = doc.as_ref().unwrap();
468    doc.should_load() as u8
469}
470
471/// Returns status of auto_load flag of this [Doc] instance. Auto loaded sub-documents automatically
472/// send a load request to their parent documents.
473#[no_mangle]
474pub unsafe extern "C" fn ydoc_auto_load(doc: *mut Doc) -> u8 {
475    let doc = doc.as_ref().unwrap();
476    doc.auto_load() as u8
477}
478
479#[repr(transparent)]
480struct CallbackState(*mut c_void);
481
482unsafe impl Send for CallbackState {}
483unsafe impl Sync for CallbackState {}
484
485impl CallbackState {
486    #[inline]
487    fn new(state: *mut c_void) -> Self {
488        CallbackState(state)
489    }
490}
491
492#[no_mangle]
493pub unsafe extern "C" fn ydoc_observe_updates_v1(
494    doc: *mut Doc,
495    state: *mut c_void,
496    cb: extern "C" fn(*mut c_void, u32, *const c_char),
497) -> *mut Subscription {
498    let state = CallbackState::new(state);
499    let doc = doc.as_ref().unwrap();
500    let subscription = doc
501        .observe_update_v1(move |_, e| {
502            let bytes = &e.update;
503            let len = bytes.len() as u32;
504            cb(state.0, len, bytes.as_ptr() as *const c_char)
505        })
506        .unwrap();
507    Box::into_raw(Box::new(subscription))
508}
509
510#[no_mangle]
511pub unsafe extern "C" fn ydoc_observe_updates_v2(
512    doc: *mut Doc,
513    state: *mut c_void,
514    cb: extern "C" fn(*mut c_void, u32, *const c_char),
515) -> *mut Subscription {
516    let state = CallbackState::new(state);
517    let doc = doc.as_ref().unwrap();
518    let subscription = doc
519        .observe_update_v2(move |_, e| {
520            let bytes = &e.update;
521            let len = bytes.len() as u32;
522            cb(state.0, len, bytes.as_ptr() as *const c_char)
523        })
524        .unwrap();
525    Box::into_raw(Box::new(subscription))
526}
527
528#[no_mangle]
529pub unsafe extern "C" fn ydoc_observe_after_transaction(
530    doc: *mut Doc,
531    state: *mut c_void,
532    cb: extern "C" fn(*mut c_void, *mut YAfterTransactionEvent),
533) -> *mut Subscription {
534    let state = CallbackState::new(state);
535    let doc = doc.as_ref().unwrap();
536    let subscription = doc
537        .observe_transaction_cleanup(move |_, e| {
538            let mut event = YAfterTransactionEvent::new(e);
539            cb(state.0, (&mut event) as *mut _);
540        })
541        .unwrap();
542    Box::into_raw(Box::new(subscription))
543}
544
545#[no_mangle]
546pub unsafe extern "C" fn ydoc_observe_subdocs(
547    doc: *mut Doc,
548    state: *mut c_void,
549    cb: extern "C" fn(*mut c_void, *mut YSubdocsEvent),
550) -> *mut Subscription {
551    let state = CallbackState::new(state);
552    let doc = doc.as_mut().unwrap();
553    let subscription = doc
554        .observe_subdocs(move |_, e| {
555            let mut event = YSubdocsEvent::new(e);
556            cb(state.0, (&mut event) as *mut _);
557        })
558        .unwrap();
559    Box::into_raw(Box::new(subscription))
560}
561
562#[no_mangle]
563pub unsafe extern "C" fn ydoc_observe_clear(
564    doc: *mut Doc,
565    state: *mut c_void,
566    cb: extern "C" fn(*mut c_void, *mut Doc),
567) -> *mut Subscription {
568    let state = CallbackState::new(state);
569    let doc = doc.as_mut().unwrap();
570    let subscription = doc
571        .observe_destroy(move |_, e| cb(state.0, e as *const Doc as *mut _))
572        .unwrap();
573    Box::into_raw(Box::new(subscription))
574}
575
576/// Manually send a load request to a parent document of this subdoc.
577#[no_mangle]
578pub unsafe extern "C" fn ydoc_load(doc: *mut Doc, parent_txn: *mut Transaction) {
579    let doc = doc.as_ref().unwrap();
580    let txn = parent_txn.as_mut().unwrap();
581    if let Some(txn) = txn.as_mut() {
582        doc.load(txn)
583    } else {
584        panic!("ydoc_load: passed read-only parent transaction, where read-write one was expected")
585    }
586}
587
588/// Destroys current document, sending a 'destroy' event and clearing up all the event callbacks
589/// registered.
590#[no_mangle]
591pub unsafe extern "C" fn ydoc_clear(doc: *mut Doc, parent_txn: *mut Transaction) {
592    let doc = doc.as_mut().unwrap();
593    let txn = parent_txn.as_mut().unwrap();
594    if let Some(txn) = txn.as_mut() {
595        doc.destroy(txn)
596    } else {
597        panic!("ydoc_clear: passed read-only parent transaction, where read-write one was expected")
598    }
599}
600
601/// Starts a new read-only transaction on a given document. All other operations happen in context
602/// of a transaction. Yrs transactions do not follow ACID rules. Once a set of operations is
603/// complete, a transaction can be finished using `ytransaction_commit` function.
604///
605/// Returns `NULL` if read-only transaction couldn't be created, i.e. when another read-write
606/// transaction is already opened.
607#[no_mangle]
608pub unsafe extern "C" fn ydoc_read_transaction(doc: *mut Doc) -> *mut Transaction {
609    assert!(!doc.is_null());
610
611    let doc = doc.as_mut().unwrap();
612    if let Ok(txn) = doc.try_transact() {
613        Box::into_raw(Box::new(Transaction::read_only(txn)))
614    } else {
615        null_mut()
616    }
617}
618
619/// Starts a new read-write transaction on a given document. All other operations happen in context
620/// of a transaction. Yrs transactions do not follow ACID rules. Once a set of operations is
621/// complete, a transaction can be finished using `ytransaction_commit` function.
622///
623/// `origin_len` and `origin` are optional parameters to specify a byte sequence used to mark
624/// the origin of this transaction (eg. you may decide to give different origins for transaction
625/// applying remote updates). These can be used by event handlers or `YUndoManager` to perform
626/// specific actions. If origin should not be set, call `ydoc_write_transaction(doc, 0, NULL)`.
627///
628/// Returns `NULL` if read-write transaction couldn't be created, i.e. when another transaction is
629/// already opened.
630#[no_mangle]
631pub unsafe extern "C" fn ydoc_write_transaction(
632    doc: *mut Doc,
633    origin_len: u32,
634    origin: *const c_char,
635) -> *mut Transaction {
636    assert!(!doc.is_null());
637
638    let doc = doc.as_mut().unwrap();
639    if origin_len == 0 {
640        if let Ok(txn) = doc.try_transact_mut() {
641            Box::into_raw(Box::new(Transaction::read_write(txn)))
642        } else {
643            null_mut()
644        }
645    } else {
646        let origin = std::slice::from_raw_parts(origin as *const u8, origin_len as usize);
647        if let Ok(txn) = doc.try_transact_mut_with(origin) {
648            Box::into_raw(Box::new(Transaction::read_write(txn)))
649        } else {
650            null_mut()
651        }
652    }
653}
654
655/// Returns a list of subdocs existing within current document.
656#[no_mangle]
657pub unsafe extern "C" fn ytransaction_subdocs(
658    txn: *mut Transaction,
659    len: *mut u32,
660) -> *mut *mut Doc {
661    let txn = txn.as_ref().unwrap();
662    let subdocs: Vec<_> = txn
663        .subdocs()
664        .map(|doc| doc as *const Doc as *mut Doc)
665        .collect();
666    let out = subdocs.into_boxed_slice();
667    *len = out.len() as u32;
668    Box::into_raw(out) as *mut _
669}
670
671/// Commit and dispose provided read-write transaction. This operation releases allocated resources,
672/// triggers update events and performs a storage compression over all operations executed in scope
673/// of a current transaction.
674#[no_mangle]
675pub unsafe extern "C" fn ytransaction_commit(txn: *mut Transaction) {
676    assert!(!txn.is_null());
677    drop(Box::from_raw(txn)); // transaction is auto-committed when dropped
678}
679
680/// Perform garbage collection of deleted blocks, even if a document was created with `skip_gc`
681/// option. This operation will scan over ALL deleted elements, NOT ONLY the ones that have been
682/// changed as part of this transaction scope.
683#[no_mangle]
684pub unsafe extern "C" fn ytransaction_force_gc(txn: *mut Transaction) {
685    assert!(!txn.is_null());
686    let txn = txn.as_mut().unwrap();
687    let txn = txn.as_mut().unwrap();
688    txn.gc(None);
689}
690
691/// Returns `1` if current transaction is of read-write type.
692/// Returns `0` if transaction is read-only.
693#[no_mangle]
694pub unsafe extern "C" fn ytransaction_writeable(txn: *mut Transaction) -> u8 {
695    assert!(!txn.is_null());
696    if txn.as_ref().unwrap().is_writeable() {
697        1
698    } else {
699        0
700    }
701}
702
703/// Evaluates a JSON path expression (see: https://en.wikipedia.org/wiki/JSONPath) on
704/// the transaction's document and returns an iterator over values matching that query.
705///
706/// Currently, this method supports the following syntax:
707/// - `$` - root object
708/// - `@` - current object
709/// - `.field` or `['field']` - member accessor
710/// - `[1]` - array index (also supports negative indices)
711/// - `.*` or `[*]` - wildcard (matches all members of an object or array)
712/// - `..` - recursive descent (matches all descendants not only direct children)
713/// - `[start:end:step]` - array slice operator (requires positive integer arguments)
714/// - `['a', 'b', 'c']` - union operator (returns an array of values for each query)
715/// - `[1, -1, 3]` - multiple indices operator (returns an array of values for each index)
716///
717/// At the moment, JSON Path does not support filter predicates.
718///
719/// Returns `NULL` if the json_path expression is invalid and couldn't be parsed.
720///
721/// Use ``yjson_path_iter_next` function in order to retrieve a consecutive array elements.
722/// Use ``yjson_path_iter_destroy` function in order to close the iterator and release its resources.
723#[no_mangle]
724pub unsafe extern "C" fn ytransaction_json_path(
725    txn: *mut Transaction,
726    json_path: *const c_char,
727) -> *mut JsonPathIter {
728    assert!(!txn.is_null());
729    let txn = txn.as_ref().unwrap();
730
731    // copy JSONPath string to have its ownership
732    let query: String = CStr::from_ptr(json_path).to_str().unwrap().into();
733    // since string is not reallocated/deallocated, we can safely pass it to the parser
734    let json_path: &'static str = unsafe { std::mem::transmute(query.as_str()) };
735    let json_path = match JsonPath::parse(json_path) {
736        Ok(query) => Box::new(query),
737        Err(_) => return null_mut(),
738    };
739    // again, we wraped parsed JSONPath in a Box to ensure that it's owned and not moving
740    let json_path_ref: &'static JsonPath = unsafe { std::mem::transmute(json_path.as_ref()) };
741    let inner = txn.json_path(json_path_ref);
742    let iter = Box::new(JsonPathIter {
743        query,
744        json_path,
745        inner,
746    });
747    Box::into_raw(iter)
748}
749
750/// Returns the next element of a JSON path iterator. If there are no more elements, `NULL` is returned.
751#[no_mangle]
752pub unsafe extern "C" fn yjson_path_iter_next(iter: *mut JsonPathIter) -> *mut YOutput {
753    assert!(!iter.is_null());
754    let iter = iter.as_mut().unwrap();
755    if let Some(value) = iter.inner.next() {
756        let youtput = YOutput::from(value);
757        Box::into_raw(Box::new(youtput))
758    } else {
759        null_mut()
760    }
761}
762
763/// Closes the JSON path iterator created via `ytransaction_json_path` and releases its resources.
764#[no_mangle]
765pub unsafe extern "C" fn yjson_path_iter_destroy(iter: *mut JsonPathIter) {
766    if !iter.is_null() {
767        drop(Box::from_raw(iter));
768    }
769}
770
771/// Gets a reference to shared data type instance at the document root-level,
772/// identified by its `name`, which must be a null-terminated UTF-8 compatible string.
773///
774/// Returns `NULL` if no such structure was defined in the document before.
775// TODO [LSViana] Rename this to `ytransaction_get_ytype()` (or similar) to match the signature.
776#[no_mangle]
777pub unsafe extern "C" fn ytype_get(txn: *mut Transaction, name: *const c_char) -> *mut Branch {
778    assert!(!txn.is_null());
779    assert!(!name.is_null());
780
781    let name = CStr::from_ptr(name).to_str().unwrap();
782    //NOTE: we're retrieving this as a text, but ultimatelly it doesn't matter as we don't define
783    // nor redefine the underlying branch type
784    if let Some(txt) = txn.as_mut().unwrap().get_text(name) {
785        txt.into_raw_branch()
786    } else {
787        null_mut()
788    }
789}
790
791/// Gets or creates a new shared `YText` data type instance as a root-level type of a given document.
792/// This structure can later be accessed using its `name`, which must be a null-terminated UTF-8
793/// compatible string.
794#[no_mangle]
795pub unsafe extern "C" fn ytext(doc: *mut Doc, name: *const c_char) -> *mut Branch {
796    assert!(!doc.is_null());
797    assert!(!name.is_null());
798
799    let name = CStr::from_ptr(name).to_str().unwrap();
800    let txt = doc.as_mut().unwrap().get_or_insert_text(name);
801    txt.into_raw_branch()
802}
803
804/// Gets or creates a new shared `YArray` data type instance as a root-level type of a given document.
805/// This structure can later be accessed using its `name`, which must be a null-terminated UTF-8
806/// compatible string.
807///
808/// Once created, a `YArray` instance will last for the entire lifecycle of a document.
809#[no_mangle]
810pub unsafe extern "C" fn yarray(doc: *mut Doc, name: *const c_char) -> *mut Branch {
811    assert!(!doc.is_null());
812    assert!(!name.is_null());
813
814    let name = CStr::from_ptr(name).to_str().unwrap();
815    doc.as_mut()
816        .unwrap()
817        .get_or_insert_array(name)
818        .into_raw_branch()
819}
820
821/// Gets or creates a new shared `YMap` data type instance as a root-level type of a given document.
822/// This structure can later be accessed using its `name`, which must be a null-terminated UTF-8
823/// compatible string.
824///
825/// Once created, a `YMap` instance will last for the entire lifecycle of a document.
826#[no_mangle]
827pub unsafe extern "C" fn ymap(doc: *mut Doc, name: *const c_char) -> *mut Branch {
828    assert!(!doc.is_null());
829    assert!(!name.is_null());
830
831    let name = CStr::from_ptr(name).to_str().unwrap();
832    doc.as_mut()
833        .unwrap()
834        .get_or_insert_map(name)
835        .into_raw_branch()
836}
837
838/// Gets or creates a new shared `YXmlElement` data type instance as a root-level type of a given
839/// document. This structure can later be accessed using its `name`, which must be a null-terminated
840/// UTF-8 compatible string.
841#[no_mangle]
842pub unsafe extern "C" fn yxmlfragment(doc: *mut Doc, name: *const c_char) -> *mut Branch {
843    assert!(!doc.is_null());
844    assert!(!name.is_null());
845
846    let name = CStr::from_ptr(name).to_str().unwrap();
847    doc.as_mut()
848        .unwrap()
849        .get_or_insert_xml_fragment(name)
850        .into_raw_branch()
851}
852
853/// Returns a state vector of a current transaction's document, serialized using lib0 version 1
854/// encoding. Payload created by this function can then be send over the network to a remote peer,
855/// where it can be used as a parameter of [ytransaction_state_diff_v1] in order to produce a delta
856/// update payload, that can be send back and applied locally in order to efficiently propagate
857/// updates from one peer to another.
858///
859/// The length of a generated binary will be passed within a `len` out parameter.
860///
861/// Once no longer needed, a returned binary can be disposed using [ybinary_destroy] function.
862#[no_mangle]
863pub unsafe extern "C" fn ytransaction_state_vector_v1(
864    txn: *const Transaction,
865    len: *mut u32,
866) -> *mut c_char {
867    assert!(!txn.is_null());
868
869    let txn = txn.as_ref().unwrap();
870    let state_vector = txn.state_vector();
871    let binary = state_vector.encode_v1().into_boxed_slice();
872
873    *len = binary.len() as u32;
874    Box::into_raw(binary) as *mut c_char
875}
876
877/// Returns a delta difference between current state of a transaction's document and a state vector
878/// `sv` encoded as a binary payload using lib0 version 1 encoding (which could be generated using
879/// [ytransaction_state_vector_v1]). Such delta can be send back to the state vector's sender in
880/// order to propagate and apply (using [ytransaction_apply]) all updates known to a current
881/// document, which remote peer was not aware of.
882///
883/// If passed `sv` pointer is null, the generated diff will be a snapshot containing entire state of
884/// the document.
885///
886/// A length of an encoded state vector payload must be passed as `sv_len` parameter.
887///
888/// A length of generated delta diff binary will be passed within a `len` out parameter.
889///
890/// Once no longer needed, a returned binary can be disposed using [ybinary_destroy] function.
891#[no_mangle]
892pub unsafe extern "C" fn ytransaction_state_diff_v1(
893    txn: *const Transaction,
894    sv: *const c_char,
895    sv_len: u32,
896    len: *mut u32,
897) -> *mut c_char {
898    assert!(!txn.is_null());
899
900    let txn = txn.as_ref().unwrap();
901    let sv = {
902        if sv.is_null() {
903            StateVector::default()
904        } else {
905            let sv_slice = std::slice::from_raw_parts(sv as *const u8, sv_len as usize);
906            if let Ok(sv) = StateVector::decode_v1(sv_slice) {
907                sv
908            } else {
909                return null_mut();
910            }
911        }
912    };
913
914    let mut encoder = EncoderV1::new();
915    txn.encode_diff(&sv, &mut encoder);
916    let binary = encoder.to_vec().into_boxed_slice();
917    *len = binary.len() as u32;
918    Box::into_raw(binary) as *mut c_char
919}
920
921/// Returns a delta difference between current state of a transaction's document and a state vector
922/// `sv` encoded as a binary payload using lib0 version 1 encoding (which could be generated using
923/// [ytransaction_state_vector_v1]). Such delta can be send back to the state vector's sender in
924/// order to propagate and apply (using [ytransaction_apply_v2]) all updates known to a current
925/// document, which remote peer was not aware of.
926///
927/// If passed `sv` pointer is null, the generated diff will be a snapshot containing entire state of
928/// the document.
929///
930/// A length of an encoded state vector payload must be passed as `sv_len` parameter.
931///
932/// A length of generated delta diff binary will be passed within a `len` out parameter.
933///
934/// Once no longer needed, a returned binary can be disposed using [ybinary_destroy] function.
935#[no_mangle]
936pub unsafe extern "C" fn ytransaction_state_diff_v2(
937    txn: *const Transaction,
938    sv: *const c_char,
939    sv_len: u32,
940    len: *mut u32,
941) -> *mut c_char {
942    assert!(!txn.is_null());
943
944    let txn = txn.as_ref().unwrap();
945    let sv = {
946        if sv.is_null() {
947            StateVector::default()
948        } else {
949            let sv_slice = std::slice::from_raw_parts(sv as *const u8, sv_len as usize);
950            if let Ok(sv) = StateVector::decode_v1(sv_slice) {
951                sv
952            } else {
953                return null_mut();
954            }
955        }
956    };
957
958    let mut encoder = EncoderV2::new();
959    txn.encode_diff(&sv, &mut encoder);
960    let binary = encoder.to_vec().into_boxed_slice();
961    *len = binary.len() as u32;
962    Box::into_raw(binary) as *mut c_char
963}
964
965/// Returns a snapshot descriptor of a current state of the document. This snapshot information
966/// can be then used to encode document data at a particular point in time
967/// (see: `ytransaction_encode_state_from_snapshot`).
968#[no_mangle]
969pub unsafe extern "C" fn ytransaction_snapshot(
970    txn: *const Transaction,
971    len: *mut u32,
972) -> *mut c_char {
973    assert!(!txn.is_null());
974    let txn = txn.as_ref().unwrap();
975    let binary = txn.snapshot().encode_v1().into_boxed_slice();
976
977    *len = binary.len() as u32;
978    Box::into_raw(binary) as *mut c_char
979}
980
981/// Encodes a state of the document at a point in time specified by the provided `snapshot`
982/// (generated by: `ytransaction_snapshot`). This is useful to generate a past view of the document.
983///
984/// The returned update is binary compatible with Yrs update lib0 v1 encoding, and can be processed
985/// with functions dedicated to work on it, like `ytransaction_apply`.
986///
987/// This function requires document with a GC option flag turned off (otherwise "time travel" would
988/// not be a safe operation). If this is not a case, the NULL pointer will be returned.
989#[no_mangle]
990pub unsafe extern "C" fn ytransaction_encode_state_from_snapshot_v1(
991    txn: *const Transaction,
992    snapshot: *const c_char,
993    snapshot_len: u32,
994    len: *mut u32,
995) -> *mut c_char {
996    assert!(!txn.is_null());
997    let txn = txn.as_ref().unwrap();
998    let snapshot = {
999        let len = snapshot_len as usize;
1000        let data = std::slice::from_raw_parts(snapshot as *mut u8, len);
1001        Snapshot::decode_v1(&data).unwrap()
1002    };
1003    let mut encoder = EncoderV1::new();
1004    match txn.encode_state_from_snapshot(&snapshot, &mut encoder) {
1005        Err(_) => null_mut(),
1006        Ok(_) => {
1007            let binary = encoder.to_vec().into_boxed_slice();
1008            *len = binary.len() as u32;
1009            Box::into_raw(binary) as *mut c_char
1010        }
1011    }
1012}
1013
1014/// Encodes a state of the document at a point in time specified by the provided `snapshot`
1015/// (generated by: `ytransaction_snapshot`). This is useful to generate a past view of the document.
1016///
1017/// The returned update is binary compatible with Yrs update lib0 v2 encoding, and can be processed
1018/// with functions dedicated to work on it, like `ytransaction_apply_v2`.
1019///
1020/// This function requires document with a GC option flag turned off (otherwise "time travel" would
1021/// not be a safe operation). If this is not a case, the NULL pointer will be returned.
1022#[no_mangle]
1023pub unsafe extern "C" fn ytransaction_encode_state_from_snapshot_v2(
1024    txn: *const Transaction,
1025    snapshot: *const c_char,
1026    snapshot_len: u32,
1027    len: *mut u32,
1028) -> *mut c_char {
1029    assert!(!txn.is_null());
1030    let txn = txn.as_ref().unwrap();
1031    let snapshot = {
1032        let len = snapshot_len as usize;
1033        let data = std::slice::from_raw_parts(snapshot as *mut u8, len);
1034        Snapshot::decode_v1(&data).unwrap()
1035    };
1036    let mut encoder = EncoderV2::new();
1037    match txn.encode_state_from_snapshot(&snapshot, &mut encoder) {
1038        Err(_) => null_mut(),
1039        Ok(_) => {
1040            let binary = encoder.to_vec().into_boxed_slice();
1041            *len = binary.len() as u32;
1042            Box::into_raw(binary) as *mut c_char
1043        }
1044    }
1045}
1046
1047/// Returns an unapplied Delete Set for the current document, waiting for missing updates in order
1048/// to be integrated into document store.
1049///
1050/// Return `NULL` if there's no missing delete set and all deletions have been applied.
1051/// See also: `ytransaction_pending_update`
1052#[no_mangle]
1053pub unsafe extern "C" fn ytransaction_pending_ds(txn: *const Transaction) -> *mut YIdSet {
1054    let txn = txn.as_ref().unwrap();
1055    match txn.store().pending_ds() {
1056        None => null_mut(),
1057        Some(ds) => Box::into_raw(Box::new(YIdSet::new(ds))),
1058    }
1059}
1060
1061#[no_mangle]
1062pub unsafe extern "C" fn ydelete_set_destroy(ds: *mut YIdSet) {
1063    if ds.is_null() {
1064        return;
1065    }
1066    drop(Box::from_raw(ds))
1067}
1068
1069/// Returns a pending update associated with an underlying `YDoc`. Pending update contains update
1070/// data waiting for being integrated into main document store. Usually reason for that is that
1071/// there were missing updates required for integration. In such cases they need to arrive and be
1072/// integrated first.
1073///
1074/// Returns `NULL` if there is not update pending. Returned value can be released by calling
1075/// `ypending_update_destroy`.
1076/// See also: `ytransaction_pending_ds`
1077#[no_mangle]
1078pub unsafe extern "C" fn ytransaction_pending_update(
1079    txn: *const Transaction,
1080) -> *mut YPendingUpdate {
1081    let txn = txn.as_ref().unwrap();
1082    match txn.store().pending_update() {
1083        None => null_mut(),
1084        Some(u) => {
1085            let binary = u.update.encode_v1().into_boxed_slice();
1086            let update_len = binary.len() as u32;
1087            let missing = YStateVector::new(&u.missing);
1088            let update = YPendingUpdate {
1089                missing,
1090                update_len,
1091                update_v1: Box::into_raw(binary) as *mut c_char,
1092            };
1093            Box::into_raw(Box::new(update))
1094        }
1095    }
1096}
1097
1098/// Structure containing unapplied update data.
1099/// Created via `ytransaction_pending_update`.
1100/// Released via `ypending_update_destroy`.
1101#[repr(C)]
1102pub struct YPendingUpdate {
1103    /// A state vector that informs about minimal client clock values that need to be satisfied
1104    /// in order to successfully apply current update.
1105    pub missing: YStateVector,
1106    /// Update data stored in lib0 v1 format.
1107    pub update_v1: *mut c_char,
1108    /// Length of `update_v1` payload.
1109    pub update_len: u32,
1110}
1111
1112#[no_mangle]
1113pub unsafe extern "C" fn ypending_update_destroy(update: *mut YPendingUpdate) {
1114    if update.is_null() {
1115        return;
1116    }
1117    let update = Box::from_raw(update);
1118    drop(update.missing);
1119    ybinary_destroy(update.update_v1, update.update_len);
1120}
1121
1122/// Returns a null-terminated UTF-8 encoded string representation of an `update` binary payload,
1123/// encoded using lib0 v1 encoding.
1124/// Returns null if update couldn't be parsed into a lib0 v1 formatting.
1125#[no_mangle]
1126pub unsafe extern "C" fn yupdate_debug_v1(update: *const c_char, update_len: u32) -> *mut c_char {
1127    assert!(!update.is_null());
1128
1129    let data = std::slice::from_raw_parts(update as *const u8, update_len as usize);
1130    if let Ok(u) = Update::decode_v1(data) {
1131        let str = format!("{:#?}", u);
1132        CString::new(str).unwrap().into_raw()
1133    } else {
1134        null_mut()
1135    }
1136}
1137
1138/// Returns a null-terminated UTF-8 encoded string representation of an `update` binary payload,
1139/// encoded using lib0 v2 encoding.
1140/// Returns null if update couldn't be parsed into a lib0 v2 formatting.
1141#[no_mangle]
1142pub unsafe extern "C" fn yupdate_debug_v2(update: *const c_char, update_len: u32) -> *mut c_char {
1143    assert!(!update.is_null());
1144
1145    let data = std::slice::from_raw_parts(update as *const u8, update_len as usize);
1146    if let Ok(u) = Update::decode_v2(data) {
1147        let str = format!("{:#?}", u);
1148        CString::new(str).unwrap().into_raw()
1149    } else {
1150        null_mut()
1151    }
1152}
1153
1154/// Applies an diff update (generated by `ytransaction_state_diff_v1`) to a local transaction's
1155/// document.
1156///
1157/// A length of generated `diff` binary must be passed within a `diff_len` out parameter.
1158///
1159/// Returns an error code in case if transaction succeeded failed:
1160/// - **0**: success
1161/// - `ERR_CODE_IO` (**1**): couldn't read data from input stream.
1162/// - `ERR_CODE_VAR_INT` (**2**): decoded variable integer outside of the expected integer size bounds.
1163/// - `ERR_CODE_EOS` (**3**): end of stream found when more data was expected.
1164/// - `ERR_CODE_UNEXPECTED_VALUE` (**4**): decoded enum tag value was not among known cases.
1165/// - `ERR_CODE_INVALID_JSON` (**5**): failure when trying to decode JSON content.
1166/// - `ERR_CODE_OTHER` (**6**): other error type than the one specified.
1167#[no_mangle]
1168pub unsafe extern "C" fn ytransaction_apply(
1169    txn: *mut Transaction,
1170    diff: *const c_char,
1171    diff_len: u32,
1172) -> u8 {
1173    assert!(!txn.is_null());
1174    assert!(!diff.is_null());
1175
1176    let update = std::slice::from_raw_parts(diff as *const u8, diff_len as usize);
1177    let mut decoder = DecoderV1::from(update);
1178    match Update::decode(&mut decoder) {
1179        Ok(update) => {
1180            let txn = txn.as_mut().unwrap();
1181            let txn = txn
1182                .as_mut()
1183                .expect("provided transaction was not writeable");
1184            match txn.apply_update(update) {
1185                Ok(_) => 0,
1186                Err(e) => update_err_code(e),
1187            }
1188        }
1189        Err(e) => err_code(e),
1190    }
1191}
1192
1193/// Applies an diff update (generated by [ytransaction_state_diff_v2]) to a local transaction's
1194/// document.
1195///
1196/// A length of generated `diff` binary must be passed within a `diff_len` out parameter.
1197///
1198/// Returns an error code in case if transaction succeeded failed:
1199/// - **0**: success
1200/// - `ERR_CODE_IO` (**1**): couldn't read data from input stream.
1201/// - `ERR_CODE_VAR_INT` (**2**): decoded variable integer outside of the expected integer size bounds.
1202/// - `ERR_CODE_EOS` (**3**): end of stream found when more data was expected.
1203/// - `ERR_CODE_UNEXPECTED_VALUE` (**4**): decoded enum tag value was not among known cases.
1204/// - `ERR_CODE_INVALID_JSON` (**5**): failure when trying to decode JSON content.
1205/// - `ERR_CODE_OTHER` (**6**): other error type than the one specified.
1206#[no_mangle]
1207pub unsafe extern "C" fn ytransaction_apply_v2(
1208    txn: *mut Transaction,
1209    diff: *const c_char,
1210    diff_len: u32,
1211) -> u8 {
1212    assert!(!txn.is_null());
1213    assert!(!diff.is_null());
1214
1215    let mut update = std::slice::from_raw_parts(diff as *const u8, diff_len as usize);
1216    match Update::decode_v2(&mut update) {
1217        Ok(update) => {
1218            let txn = txn.as_mut().unwrap();
1219            let txn = txn
1220                .as_mut()
1221                .expect("provided transaction was not writeable");
1222            match txn.apply_update(update) {
1223                Ok(_) => 0,
1224                Err(e) => update_err_code(e),
1225            }
1226        }
1227        Err(e) => err_code(e),
1228    }
1229}
1230
1231/// Error code: couldn't read data from input stream.
1232pub const ERR_CODE_IO: u8 = 1;
1233
1234/// Error code: decoded variable integer outside of the expected integer size bounds.
1235pub const ERR_CODE_VAR_INT: u8 = 2;
1236
1237/// Error code: end of stream found when more data was expected.
1238pub const ERR_CODE_EOS: u8 = 3;
1239
1240/// Error code: decoded enum tag value was not among known cases.
1241pub const ERR_CODE_UNEXPECTED_VALUE: u8 = 4;
1242
1243/// Error code: failure when trying to decode JSON content.
1244pub const ERR_CODE_INVALID_JSON: u8 = 5;
1245
1246/// Error code: other error type than the one specified.
1247pub const ERR_CODE_OTHER: u8 = 6;
1248
1249/// Error code: not enough memory to perform an operation.
1250pub const ERR_NOT_ENOUGH_MEMORY: u8 = 7;
1251
1252/// Error code: conversion attempt to specific Rust type was not possible.
1253pub const ERR_TYPE_MISMATCH: u8 = 8;
1254
1255/// Error code: miscellaneous error coming from serde, not covered by other error codes.
1256pub const ERR_CUSTOM: u8 = 9;
1257
1258/// Error code: update block assigned to parent that is not a valid shared ref of deleted block.
1259pub const ERR_INVALID_PARENT: u8 = 9;
1260
1261fn err_code(e: Error) -> u8 {
1262    match e {
1263        Error::InvalidVarInt => ERR_CODE_VAR_INT,
1264        Error::EndOfBuffer(_) => ERR_CODE_EOS,
1265        Error::UnexpectedValue => ERR_CODE_UNEXPECTED_VALUE,
1266        Error::InvalidJSON(_) => ERR_CODE_INVALID_JSON,
1267        Error::NotEnoughMemory(_) => ERR_NOT_ENOUGH_MEMORY,
1268        Error::TypeMismatch(_) => ERR_TYPE_MISMATCH,
1269        Error::Custom(_) => ERR_CUSTOM,
1270    }
1271}
1272fn update_err_code(e: UpdateError) -> u8 {
1273    match e {
1274        UpdateError::InvalidParent(_, _) => ERR_INVALID_PARENT,
1275    }
1276}
1277
1278/// Returns the length of the `YText` string content in bytes (without the null terminator character)
1279#[no_mangle]
1280pub unsafe extern "C" fn ytext_len(txt: *const Branch, txn: *const Transaction) -> u32 {
1281    assert!(!txt.is_null());
1282    let txn = txn.as_ref().unwrap();
1283    let txt = TextRef::from_raw_branch(txt);
1284    txt.len(txn)
1285}
1286
1287/// Returns a null-terminated UTF-8 encoded string content of a current `YText` shared data type.
1288///
1289/// Generated string resources should be released using [ystring_destroy] function.
1290#[no_mangle]
1291pub unsafe extern "C" fn ytext_string(txt: *const Branch, txn: *const Transaction) -> *mut c_char {
1292    assert!(!txt.is_null());
1293
1294    let txn = txn.as_ref().unwrap();
1295    let txt = TextRef::from_raw_branch(txt);
1296    let str = txt.get_string(txn);
1297    CString::new(str).unwrap().into_raw()
1298}
1299
1300/// Inserts a null-terminated UTF-8 encoded string a given `index`. `index` value must be between
1301/// 0 and a length of a `YText` (inclusive, accordingly to [ytext_len] return value), otherwise this
1302/// function will panic.
1303///
1304/// A `str` parameter must be a null-terminated UTF-8 encoded string. This function doesn't take
1305/// ownership over a passed value - it will be copied and therefore a string parameter must be
1306/// released by the caller.
1307///
1308/// A nullable pointer with defined `attrs` will be used to wrap provided text with
1309/// a formatting blocks. `attrs` must be a map-like type.
1310#[no_mangle]
1311pub unsafe extern "C" fn ytext_insert(
1312    txt: *const Branch,
1313    txn: *mut Transaction,
1314    index: u32,
1315    value: *const c_char,
1316    attrs: *const YInput,
1317) {
1318    assert!(!txt.is_null());
1319    assert!(!txn.is_null());
1320    assert!(!value.is_null());
1321
1322    let chunk = CStr::from_ptr(value).to_str().unwrap();
1323    let txn = txn.as_mut().unwrap();
1324    let txn = txn
1325        .as_mut()
1326        .expect("provided transaction was not writeable");
1327    let txt = TextRef::from_raw_branch(txt);
1328    let index = index as u32;
1329    if attrs.is_null() {
1330        txt.insert(txn, index, chunk)
1331    } else {
1332        if let Some(attrs) = map_attrs(attrs.read().into()) {
1333            txt.insert_with_attributes(txn, index, chunk, attrs)
1334        } else {
1335            panic!("ytext_insert: passed attributes are not of map type")
1336        }
1337    }
1338}
1339
1340/// Wraps an existing piece of text within a range described by `index`-`len` parameters with
1341/// formatting blocks containing provided `attrs` metadata. `attrs` must be a map-like type.
1342#[no_mangle]
1343pub unsafe extern "C" fn ytext_format(
1344    txt: *const Branch,
1345    txn: *mut Transaction,
1346    index: u32,
1347    len: u32,
1348    attrs: *const YInput,
1349) {
1350    assert!(!txt.is_null());
1351    assert!(!txn.is_null());
1352    assert!(!attrs.is_null());
1353
1354    if let Some(attrs) = map_attrs(attrs.read().into()) {
1355        let txt = TextRef::from_raw_branch(txt);
1356        let txn = txn.as_mut().unwrap();
1357        let txn = txn
1358            .as_mut()
1359            .expect("provided transaction was not writeable");
1360        let index = index as u32;
1361        let len = len as u32;
1362        txt.format(txn, index, len, attrs);
1363    } else {
1364        panic!("ytext_format: passed attributes are not of map type")
1365    }
1366}
1367
1368/// Inserts an embed content given `index`. `index` value must be between 0 and a length of a
1369/// `YText` (inclusive, accordingly to [ytext_len] return value), otherwise this
1370/// function will panic.
1371///
1372/// A `str` parameter must be a null-terminated UTF-8 encoded string. This function doesn't take
1373/// ownership over a passed value - it will be copied and therefore a string parameter must be
1374/// released by the caller.
1375///
1376/// A nullable pointer with defined `attrs` will be used to wrap provided text with
1377/// a formatting blocks. `attrs` must be a map-like type.
1378#[no_mangle]
1379pub unsafe extern "C" fn ytext_insert_embed(
1380    txt: *const Branch,
1381    txn: *mut Transaction,
1382    index: u32,
1383    content: *const YInput,
1384    attrs: *const YInput,
1385) {
1386    assert!(!txt.is_null());
1387    assert!(!txn.is_null());
1388    assert!(!content.is_null());
1389
1390    let txn = txn.as_mut().unwrap();
1391    let txn = txn
1392        .as_mut()
1393        .expect("provided transaction was not writeable");
1394    let txt = TextRef::from_raw_branch(txt);
1395    let index = index as u32;
1396    let content = content.read();
1397    if attrs.is_null() {
1398        txt.insert_embed(txn, index, content);
1399    } else {
1400        if let Some(attrs) = map_attrs(attrs.read().into()) {
1401            txt.insert_embed_with_attributes(txn, index, content, attrs);
1402        } else {
1403            panic!("ytext_insert_embed: passed attributes are not of map type")
1404        }
1405    }
1406}
1407
1408/// Performs a series of changes over the given `YText` shared ref type, described by the `delta`
1409/// parameter:
1410///
1411/// - Deltas constructed with `ydelta_input_retain` will move cursor position by the given number
1412///   of elements. If formatting attributes were defined, all elements skipped over this way will be
1413///   wrapped by given formatting attributes.
1414/// - Deltas constructed with `ydelta_input_delete` will tell cursor to remove a corresponding
1415///   number of elements.
1416/// - Deltas constructed with `ydelta_input_insert` will tell cursor to insert given elements into
1417///   current cursor position. While these elements can be of any type (used for embedding ie.
1418///   shared types or binary payload like images), for the text insertion a `yinput_string`
1419///   is expected. If formatting attributes were specified, inserted elements will be wrapped by
1420///   given formatting attributes.
1421#[no_mangle]
1422pub unsafe extern "C" fn ytext_insert_delta(
1423    txt: *const Branch,
1424    txn: *mut Transaction,
1425    delta: *mut YDeltaIn,
1426    delta_len: u32,
1427) {
1428    let txt = TextRef::from_raw_branch(txt);
1429    let txn = txn.as_mut().unwrap();
1430    let txn = txn
1431        .as_mut()
1432        .expect("provided transaction was not writeable");
1433    let delta = std::slice::from_raw_parts(delta, delta_len as usize);
1434    let mut insert = Vec::with_capacity(delta.len());
1435    for chunk in delta {
1436        let d = chunk.as_input();
1437        insert.push(d);
1438    }
1439    txt.apply_delta(txn, insert);
1440}
1441
1442/// Creates a parameter for `ytext_insert_delta` function. This parameter will move cursor position
1443/// by the `len` of elements. If formatting `attrs` were defined, all elements skipped over this
1444/// way will be wrapped by given formatting attributes.
1445#[no_mangle]
1446pub unsafe extern "C" fn ydelta_input_retain(len: u32, attrs: *const YInput) -> YDeltaIn {
1447    YDeltaIn {
1448        tag: Y_EVENT_CHANGE_RETAIN,
1449        len,
1450        attributes: attrs,
1451        insert: null(),
1452    }
1453}
1454
1455/// Creates a parameter for `ytext_insert_delta` function. This parameter will tell cursor to remove
1456/// a corresponding number of elements, starting from current cursor position.
1457#[no_mangle]
1458pub unsafe extern "C" fn ydelta_input_delete(len: u32) -> YDeltaIn {
1459    YDeltaIn {
1460        tag: Y_EVENT_CHANGE_DELETE,
1461        len,
1462        attributes: null(),
1463        insert: null(),
1464    }
1465}
1466
1467/// Creates a parameter for `ytext_insert_delta` function. This parameter will tell cursor to insert
1468/// given elements into current cursor position. While these elements can be of any type (used for
1469/// embedding ie. shared types or binary payload like images), for the text insertion a `yinput_string`
1470/// is expected. If formatting attributes were specified, inserted elements will be wrapped by
1471/// given formatting attributes.
1472#[no_mangle]
1473pub unsafe extern "C" fn ydelta_input_insert(
1474    data: *const YInput,
1475    attrs: *const YInput,
1476) -> YDeltaIn {
1477    YDeltaIn {
1478        tag: Y_EVENT_CHANGE_ADD,
1479        len: 1,
1480        attributes: attrs,
1481        insert: data,
1482    }
1483}
1484
1485fn map_attrs(attrs: Any) -> Option<Attrs> {
1486    if let Any::Map(attrs) = attrs {
1487        let attrs = attrs
1488            .iter()
1489            .map(|(k, v)| (k.as_str().into(), v.clone()))
1490            .collect();
1491        Some(attrs)
1492    } else {
1493        None
1494    }
1495}
1496
1497/// Removes a range of characters, starting a a given `index`. This range must fit within the bounds
1498/// of a current `YText`, otherwise this function call will fail.
1499///
1500/// An `index` value must be between 0 and the length of a `YText` (exclusive, accordingly to
1501/// [ytext_len] return value).
1502///
1503/// A `length` must be lower or equal number of characters (counted as UTF chars depending on the
1504/// encoding configured by `YDoc`) from `index` position to the end of of the string.
1505#[no_mangle]
1506pub unsafe extern "C" fn ytext_remove_range(
1507    txt: *const Branch,
1508    txn: *mut Transaction,
1509    index: u32,
1510    length: u32,
1511) {
1512    assert!(!txt.is_null());
1513    assert!(!txn.is_null());
1514
1515    let txn = txn.as_mut().unwrap();
1516    let txn = txn
1517        .as_mut()
1518        .expect("provided transaction was not writeable");
1519    let txt = TextRef::from_raw_branch(txt);
1520    txt.remove_range(txn, index as u32, length as u32)
1521}
1522
1523/// Returns a number of elements stored within current instance of `YArray`.
1524#[no_mangle]
1525pub unsafe extern "C" fn yarray_len(array: *const Branch) -> u32 {
1526    assert!(!array.is_null());
1527
1528    let array = array.as_ref().unwrap();
1529    array.len() as u32
1530}
1531
1532/// Returns a pointer to a `YOutput` value stored at a given `index` of a current `YArray`.
1533/// If `index` is outside the bounds of an array, a null pointer will be returned.
1534///
1535/// A value returned should be eventually released using [youtput_destroy] function.
1536#[no_mangle]
1537pub unsafe extern "C" fn yarray_get(
1538    array: *const Branch,
1539    txn: *const Transaction,
1540    index: u32,
1541) -> *mut YOutput {
1542    assert!(!array.is_null());
1543
1544    let array = ArrayRef::from_raw_branch(array);
1545    let txn = txn.as_ref().unwrap();
1546
1547    if let Some(val) = array.get(txn, index as u32) {
1548        Box::into_raw(Box::new(YOutput::from(val)))
1549    } else {
1550        std::ptr::null_mut()
1551    }
1552}
1553
1554/// Returns a UTF-8 encoded, NULL-terminated JSON string representing a value stored in a current
1555/// YArray under a given index.
1556///
1557/// This method will return `NULL` pointer if value was outside the bound of an array or couldn't be
1558/// serialized into JSON string.
1559///
1560/// This method will also try to serialize complex types that don't have native JSON representation
1561/// like YMap, YArray, YText etc. in such cases their contents will be materialized into JSON values.
1562///
1563/// A string returned should be eventually released using [ystring_destroy] function.
1564#[no_mangle]
1565pub unsafe extern "C" fn yarray_get_json(
1566    array: *const Branch,
1567    txn: *const Transaction,
1568    index: u32,
1569) -> *mut c_char {
1570    assert!(!array.is_null());
1571
1572    let array = ArrayRef::from_raw_branch(array);
1573    let txn = txn.as_ref().unwrap();
1574
1575    if let Some(val) = array.get(txn, index as u32) {
1576        let any = val.to_json(txn);
1577        let json = match serde_json::to_string(&any) {
1578            Ok(json) => json,
1579            Err(_) => return std::ptr::null_mut(),
1580        };
1581        CString::new(json).unwrap().into_raw()
1582    } else {
1583        std::ptr::null_mut()
1584    }
1585}
1586
1587/// Inserts a range of `items` into current `YArray`, starting at given `index`. An `items_len`
1588/// parameter is used to determine the size of `items` array - it can also be used to insert
1589/// a single element given its pointer.
1590///
1591/// An `index` value must be between 0 and (inclusive) length of a current array (use [yarray_len]
1592/// to determine its length), otherwise it will panic at runtime.
1593///
1594/// `YArray` doesn't take ownership over the inserted `items` data - their contents are being copied
1595/// into array structure - therefore caller is responsible for freeing all memory associated with
1596/// input params.
1597#[no_mangle]
1598pub unsafe extern "C" fn yarray_insert_range(
1599    array: *const Branch,
1600    txn: *mut Transaction,
1601    index: u32,
1602    items: *const YInput,
1603    items_len: u32,
1604) {
1605    assert!(!array.is_null());
1606    assert!(!txn.is_null());
1607    assert!(!items.is_null());
1608
1609    let array = ArrayRef::from_raw_branch(array);
1610    let txn = txn.as_mut().unwrap();
1611    let txn = txn
1612        .as_mut()
1613        .expect("provided transaction was not writeable");
1614
1615    let ptr = items;
1616    let mut i = 0;
1617    let mut j = index as u32;
1618    let len = items_len as isize;
1619    while i < len {
1620        let mut vec: Vec<Any> = Vec::default();
1621
1622        // try read as many values a JSON-like primitives and insert them at once
1623        while i < len {
1624            let val = ptr.offset(i).read();
1625            if val.tag <= 0 {
1626                let any = val.into();
1627                vec.push(any);
1628            } else {
1629                break;
1630            }
1631            i += 1;
1632        }
1633
1634        if !vec.is_empty() {
1635            let len = vec.len() as u32;
1636            array.insert_range(txn, j, vec);
1637            j += len;
1638        } else {
1639            let val = ptr.offset(i).read();
1640            array.insert(txn, j, val);
1641            i += 1;
1642            j += 1;
1643        }
1644    }
1645}
1646
1647/// Removes a `len` of consecutive range of elements from current `array` instance, starting at
1648/// a given `index`. Range determined by `index` and `len` must fit into boundaries of an array,
1649/// otherwise it will panic at runtime.
1650#[no_mangle]
1651pub unsafe extern "C" fn yarray_remove_range(
1652    array: *const Branch,
1653    txn: *mut Transaction,
1654    index: u32,
1655    len: u32,
1656) {
1657    assert!(!array.is_null());
1658    assert!(!txn.is_null());
1659
1660    let array = ArrayRef::from_raw_branch(array);
1661    let txn = txn.as_mut().unwrap();
1662    let txn = txn
1663        .as_mut()
1664        .expect("provided transaction was not writeable");
1665
1666    array.remove_range(txn, index as u32, len as u32)
1667}
1668
1669#[no_mangle]
1670pub unsafe extern "C" fn yarray_move(
1671    array: *const Branch,
1672    txn: *mut Transaction,
1673    source: u32,
1674    target: u32,
1675) {
1676    assert!(!array.is_null());
1677    assert!(!txn.is_null());
1678
1679    let array = ArrayRef::from_raw_branch(array);
1680    let txn = txn.as_mut().unwrap();
1681    let txn = txn
1682        .as_mut()
1683        .expect("provided transaction was not writeable");
1684
1685    array.move_to(txn, source as u32, target as u32)
1686}
1687
1688/// Returns an iterator, which can be used to traverse over all elements of an `array` (`array`'s
1689/// length can be determined using [yarray_len] function).
1690///
1691/// Use [yarray_iter_next] function in order to retrieve a consecutive array elements.
1692/// Use [yarray_iter_destroy] function in order to close the iterator and release its resources.
1693#[no_mangle]
1694pub unsafe extern "C" fn yarray_iter(
1695    array: *const Branch,
1696    txn: *mut Transaction,
1697) -> *mut ArrayIter {
1698    assert!(!array.is_null());
1699    assert!(!txn.is_null());
1700
1701    let txn = txn.as_ref().unwrap();
1702    let array = &ArrayRef::from_raw_branch(array) as *const ArrayRef;
1703    Box::into_raw(Box::new(ArrayIter(array.as_ref().unwrap().iter(txn))))
1704}
1705
1706/// Releases all of an `YArray` iterator resources created by calling [yarray_iter].
1707#[no_mangle]
1708pub unsafe extern "C" fn yarray_iter_destroy(iter: *mut ArrayIter) {
1709    if !iter.is_null() {
1710        drop(Box::from_raw(iter))
1711    }
1712}
1713
1714/// Moves current `YArray` iterator over to a next element, returning a pointer to it. If an iterator
1715/// comes to an end of an array, a null pointer will be returned.
1716///
1717/// Returned values should be eventually released using [youtput_destroy] function.
1718#[no_mangle]
1719pub unsafe extern "C" fn yarray_iter_next(iterator: *mut ArrayIter) -> *mut YOutput {
1720    assert!(!iterator.is_null());
1721
1722    let iter = iterator.as_mut().unwrap();
1723    if let Some(v) = iter.0.next() {
1724        let out = YOutput::from(v);
1725        Box::into_raw(Box::new(out))
1726    } else {
1727        std::ptr::null_mut()
1728    }
1729}
1730
1731/// Returns an iterator, which can be used to traverse over all key-value pairs of a `map`.
1732///
1733/// Use [ymap_iter_next] function in order to retrieve a consecutive (**unordered**) map entries.
1734/// Use [ymap_iter_destroy] function in order to close the iterator and release its resources.
1735#[no_mangle]
1736pub unsafe extern "C" fn ymap_iter(map: *const Branch, txn: *const Transaction) -> *mut MapIter {
1737    assert!(!map.is_null());
1738
1739    let txn = txn.as_ref().unwrap();
1740    let map = &MapRef::from_raw_branch(map) as *const MapRef;
1741    Box::into_raw(Box::new(MapIter(map.as_ref().unwrap().iter(txn))))
1742}
1743
1744/// Releases all of an `YMap` iterator resources created by calling [ymap_iter].
1745#[no_mangle]
1746pub unsafe extern "C" fn ymap_iter_destroy(iter: *mut MapIter) {
1747    if !iter.is_null() {
1748        drop(Box::from_raw(iter))
1749    }
1750}
1751
1752/// Moves current `YMap` iterator over to a next entry, returning a pointer to it. If an iterator
1753/// comes to an end of a map, a null pointer will be returned. Yrs maps are unordered and so are
1754/// their iterators.
1755///
1756/// Returned values should be eventually released using [ymap_entry_destroy] function.
1757#[no_mangle]
1758pub unsafe extern "C" fn ymap_iter_next(iter: *mut MapIter) -> *mut YMapEntry {
1759    assert!(!iter.is_null());
1760
1761    let iter = iter.as_mut().unwrap();
1762    if let Some((key, value)) = iter.0.next() {
1763        let output = YOutput::from(value);
1764        Box::into_raw(Box::new(YMapEntry::new(key, Box::new(output))))
1765    } else {
1766        std::ptr::null_mut()
1767    }
1768}
1769
1770/// Returns a number of entries stored within a `map`.
1771#[no_mangle]
1772pub unsafe extern "C" fn ymap_len(map: *const Branch, txn: *const Transaction) -> u32 {
1773    assert!(!map.is_null());
1774
1775    let txn = txn.as_ref().unwrap();
1776    let map = MapRef::from_raw_branch(map);
1777
1778    map.len(txn)
1779}
1780
1781/// Inserts a new entry (specified as `key`-`value` pair) into a current `map`. If entry under such
1782/// given `key` already existed, its corresponding value will be replaced.
1783///
1784/// A `key` must be a null-terminated UTF-8 encoded string, which contents will be copied into
1785/// a `map` (therefore it must be freed by the function caller).
1786///
1787/// A `value` content is being copied into a `map`, therefore any of its content must be freed by
1788/// the function caller.
1789#[no_mangle]
1790pub unsafe extern "C" fn ymap_insert(
1791    map: *const Branch,
1792    txn: *mut Transaction,
1793    key: *const c_char,
1794    value: *const YInput,
1795) {
1796    assert!(!map.is_null());
1797    assert!(!txn.is_null());
1798    assert!(!key.is_null());
1799    assert!(!value.is_null());
1800
1801    let cstr = CStr::from_ptr(key);
1802    let key = cstr.to_str().unwrap().to_string();
1803
1804    let map = MapRef::from_raw_branch(map);
1805    let txn = txn.as_mut().unwrap();
1806    let txn = txn
1807        .as_mut()
1808        .expect("provided transaction was not writeable");
1809
1810    map.insert(txn, key, value.read());
1811}
1812
1813/// Removes a `map` entry, given its `key`. Returns `1` if the corresponding entry was successfully
1814/// removed or `0` if no entry with a provided `key` has been found inside of a `map`.
1815///
1816/// A `key` must be a null-terminated UTF-8 encoded string.
1817#[no_mangle]
1818pub unsafe extern "C" fn ymap_remove(
1819    map: *const Branch,
1820    txn: *mut Transaction,
1821    key: *const c_char,
1822) -> u8 {
1823    assert!(!map.is_null());
1824    assert!(!txn.is_null());
1825    assert!(!key.is_null());
1826
1827    let key = CStr::from_ptr(key).to_str().unwrap();
1828
1829    let map = MapRef::from_raw_branch(map);
1830    let txn = txn.as_mut().unwrap();
1831    let txn = txn
1832        .as_mut()
1833        .expect("provided transaction was not writeable");
1834
1835    if let Some(_) = map.remove(txn, key) {
1836        Y_TRUE
1837    } else {
1838        Y_FALSE
1839    }
1840}
1841
1842/// Returns a value stored under the provided `key`, or a null pointer if no entry with such `key`
1843/// has been found in a current `map`. A returned value is allocated by this function and therefore
1844/// should be eventually released using [youtput_destroy] function.
1845///
1846/// A `key` must be a null-terminated UTF-8 encoded string.
1847#[no_mangle]
1848pub unsafe extern "C" fn ymap_get(
1849    map: *const Branch,
1850    txn: *const Transaction,
1851    key: *const c_char,
1852) -> *mut YOutput {
1853    assert!(!map.is_null());
1854    assert!(!key.is_null());
1855    assert!(!txn.is_null());
1856
1857    let txn = txn.as_ref().unwrap();
1858    let key = CStr::from_ptr(key).to_str().unwrap();
1859
1860    let map = MapRef::from_raw_branch(map);
1861
1862    if let Some(value) = map.get(txn, key) {
1863        let output = YOutput::from(value);
1864        Box::into_raw(Box::new(output))
1865    } else {
1866        std::ptr::null_mut()
1867    }
1868}
1869
1870/// Returns a value stored under the provided `key` as UTF-8 encoded, NULL-terminated JSON string.
1871/// Once not needed that string should be deallocated using `ystring_destroy`.
1872///
1873/// This method will return `NULL` pointer if value was not found or value couldn't be serialized
1874/// into JSON string.
1875///
1876/// This method will also try to serialize complex types that don't have native JSON representation
1877/// like YMap, YArray, YText etc. in such cases their contents will be materialized into JSON values.
1878#[no_mangle]
1879pub unsafe extern "C" fn ymap_get_json(
1880    map: *const Branch,
1881    txn: *const Transaction,
1882    key: *const c_char,
1883) -> *mut c_char {
1884    assert!(!map.is_null());
1885    assert!(!key.is_null());
1886    assert!(!txn.is_null());
1887
1888    let txn = txn.as_ref().unwrap();
1889    let key = CStr::from_ptr(key).to_str().unwrap();
1890
1891    let map = MapRef::from_raw_branch(map);
1892
1893    if let Some(value) = map.get(txn, key) {
1894        let any = value.to_json(txn);
1895        match serde_json::to_string(&any) {
1896            Ok(json) => CString::new(json).unwrap().into_raw(),
1897            Err(_) => std::ptr::null_mut(),
1898        }
1899    } else {
1900        std::ptr::null_mut()
1901    }
1902}
1903
1904/// Removes all entries from a current `map`.
1905#[no_mangle]
1906pub unsafe extern "C" fn ymap_remove_all(map: *const Branch, txn: *mut Transaction) {
1907    assert!(!map.is_null());
1908    assert!(!txn.is_null());
1909
1910    let map = MapRef::from_raw_branch(map);
1911    let txn = txn.as_mut().unwrap();
1912    let txn = txn
1913        .as_mut()
1914        .expect("provided transaction was not writeable");
1915
1916    map.clear(txn);
1917}
1918
1919/// Return a name (or an XML tag) of a current `YXmlElement`. Root-level XML nodes use "UNDEFINED" as
1920/// their tag names.
1921///
1922/// Returned value is a null-terminated UTF-8 string, which must be released using [ystring_destroy]
1923/// function.
1924#[no_mangle]
1925pub unsafe extern "C" fn yxmlelem_tag(xml: *const Branch) -> *mut c_char {
1926    assert!(!xml.is_null());
1927    let xml = XmlElementRef::from_raw_branch(xml);
1928    if let Some(tag) = xml.try_tag() {
1929        CString::new(tag.deref()).unwrap().into_raw()
1930    } else {
1931        null_mut()
1932    }
1933}
1934
1935/// Converts current `YXmlElement` together with its children and attributes into a flat string
1936/// representation (no padding) eg. `<UNDEFINED><title key="value">sample text</title></UNDEFINED>`.
1937///
1938/// Returned value is a null-terminated UTF-8 string, which must be released using [ystring_destroy]
1939/// function.
1940#[no_mangle]
1941pub unsafe extern "C" fn yxmlelem_string(
1942    xml: *const Branch,
1943    txn: *const Transaction,
1944) -> *mut c_char {
1945    assert!(!xml.is_null());
1946    assert!(!txn.is_null());
1947
1948    let txn = txn.as_ref().unwrap();
1949    let xml = XmlElementRef::from_raw_branch(xml);
1950
1951    let str = xml.get_string(txn);
1952    CString::new(str).unwrap().into_raw()
1953}
1954
1955/// Inserts an XML attribute described using `attr_name` and `attr_value`. If another attribute with
1956/// the same name already existed, its value will be replaced with a provided one.
1957///
1958/// Both `attr_name` and `attr_value` must be a null-terminated UTF-8 encoded strings. Their
1959/// contents are being copied, therefore it's up to a function caller to properly release them.
1960#[no_mangle]
1961pub unsafe extern "C" fn yxmlelem_insert_attr(
1962    xml: *const Branch,
1963    txn: *mut Transaction,
1964    attr_name: *const c_char,
1965    attr_value: *const YInput,
1966) {
1967    assert!(!xml.is_null());
1968    assert!(!txn.is_null());
1969    assert!(!attr_name.is_null());
1970    assert!(!attr_value.is_null());
1971
1972    let xml = XmlElementRef::from_raw_branch(xml);
1973    let txn = txn.as_mut().unwrap();
1974    let txn = txn
1975        .as_mut()
1976        .expect("provided transaction was not writeable");
1977
1978    let key = CStr::from_ptr(attr_name).to_str().unwrap();
1979
1980    xml.insert_attribute(txn, key, attr_value.read());
1981}
1982
1983/// Removes an attribute from a current `YXmlElement`, given its name.
1984///
1985/// An `attr_name`must be a null-terminated UTF-8 encoded string.
1986#[no_mangle]
1987pub unsafe extern "C" fn yxmlelem_remove_attr(
1988    xml: *const Branch,
1989    txn: *mut Transaction,
1990    attr_name: *const c_char,
1991) {
1992    assert!(!xml.is_null());
1993    assert!(!txn.is_null());
1994    assert!(!attr_name.is_null());
1995
1996    let xml = XmlElementRef::from_raw_branch(xml);
1997    let txn = txn.as_mut().unwrap();
1998    let txn = txn
1999        .as_mut()
2000        .expect("provided transaction was not writeable");
2001
2002    let key = CStr::from_ptr(attr_name).to_str().unwrap();
2003    xml.remove_attribute(txn, &key);
2004}
2005
2006/// Returns the value of a current `YXmlElement`, given its name, or a null pointer if not attribute
2007/// with such name has been found. Returned pointer is a null-terminated UTF-8 encoded string, which
2008/// should be released using [ystring_destroy] function.
2009///
2010/// An `attr_name` must be a null-terminated UTF-8 encoded string.
2011#[no_mangle]
2012pub unsafe extern "C" fn yxmlelem_get_attr(
2013    xml: *const Branch,
2014    txn: *const Transaction,
2015    attr_name: *const c_char,
2016) -> *mut YOutput {
2017    assert!(!xml.is_null());
2018    assert!(!attr_name.is_null());
2019    assert!(!txn.is_null());
2020
2021    let xml = XmlElementRef::from_raw_branch(xml);
2022
2023    let key = CStr::from_ptr(attr_name).to_str().unwrap();
2024    let txn = txn.as_ref().unwrap();
2025    if let Some(value) = xml.get_attribute(txn, key) {
2026        let output = YOutput::from(value);
2027        Box::into_raw(Box::new(output))
2028    } else {
2029        std::ptr::null_mut()
2030    }
2031}
2032
2033/// Returns an iterator over the `YXmlElement` attributes.
2034///
2035/// Use [yxmlattr_iter_next] function in order to retrieve a consecutive (**unordered**) attributes.
2036/// Use [yxmlattr_iter_destroy] function in order to close the iterator and release its resources.
2037#[no_mangle]
2038pub unsafe extern "C" fn yxmlelem_attr_iter(
2039    xml: *const Branch,
2040    txn: *const Transaction,
2041) -> *mut Attributes {
2042    assert!(!xml.is_null());
2043    assert!(!txn.is_null());
2044
2045    let xml = &XmlElementRef::from_raw_branch(xml) as *const XmlElementRef;
2046    let txn = txn.as_ref().unwrap();
2047    Box::into_raw(Box::new(Attributes(xml.as_ref().unwrap().attributes(txn))))
2048}
2049
2050/// Returns an iterator over the `YXmlText` attributes.
2051///
2052/// Use [yxmlattr_iter_next] function in order to retrieve a consecutive (**unordered**) attributes.
2053/// Use [yxmlattr_iter_destroy] function in order to close the iterator and release its resources.
2054#[no_mangle]
2055pub unsafe extern "C" fn yxmltext_attr_iter(
2056    xml: *const Branch,
2057    txn: *const Transaction,
2058) -> *mut Attributes {
2059    assert!(!xml.is_null());
2060    assert!(!txn.is_null());
2061
2062    let xml = &XmlTextRef::from_raw_branch(xml) as *const XmlTextRef;
2063    let txn = txn.as_ref().unwrap();
2064    Box::into_raw(Box::new(Attributes(xml.as_ref().unwrap().attributes(txn))))
2065}
2066
2067/// Releases all of attributes iterator resources created by calling [yxmlelem_attr_iter]
2068/// or [yxmltext_attr_iter].
2069#[no_mangle]
2070pub unsafe extern "C" fn yxmlattr_iter_destroy(iterator: *mut Attributes) {
2071    if !iterator.is_null() {
2072        drop(Box::from_raw(iterator))
2073    }
2074}
2075
2076/// Returns a next XML attribute from an `iterator`. Attributes are returned in an unordered
2077/// manner. Once `iterator` reaches the end of attributes collection, a null pointer will be
2078/// returned.
2079///
2080/// Returned value should be eventually released using [yxmlattr_destroy].
2081#[no_mangle]
2082pub unsafe extern "C" fn yxmlattr_iter_next(iterator: *mut Attributes) -> *mut YXmlAttr {
2083    assert!(!iterator.is_null());
2084
2085    let iter = iterator.as_mut().unwrap();
2086
2087    if let Some((name, value)) = iter.0.next() {
2088        Box::into_raw(Box::new(YXmlAttr {
2089            name: CString::new(name).unwrap().into_raw(),
2090            value: Box::into_raw(Box::new(YOutput::from(value))),
2091        }))
2092    } else {
2093        std::ptr::null_mut()
2094    }
2095}
2096
2097/// Returns a next sibling of a current XML node, which can be either another `YXmlElement`
2098/// or a `YXmlText`. Together with [yxmlelem_first_child] it may be used to iterate over the direct
2099/// children of an XML node (in order to iterate over the nested XML structure use
2100/// [yxmlelem_tree_walker]).
2101///
2102/// If current `YXmlElement` is the last child, this function returns a null pointer.
2103/// A returned value should be eventually released using [youtput_destroy] function.
2104#[no_mangle]
2105pub unsafe extern "C" fn yxml_next_sibling(
2106    xml: *const Branch,
2107    txn: *const Transaction,
2108) -> *mut YOutput {
2109    assert!(!xml.is_null());
2110    assert!(!txn.is_null());
2111
2112    let xml = XmlElementRef::from_raw_branch(xml);
2113    let txn = txn.as_ref().unwrap();
2114
2115    let mut siblings = xml.siblings(txn);
2116    if let Some(next) = siblings.next() {
2117        match next {
2118            XmlOut::Element(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlElement(v)))),
2119            XmlOut::Text(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlText(v)))),
2120            XmlOut::Fragment(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlFragment(v)))),
2121        }
2122    } else {
2123        null_mut()
2124    }
2125}
2126
2127/// Returns a previous sibling of a current XML node, which can be either another `YXmlElement`
2128/// or a `YXmlText`.
2129///
2130/// If current `YXmlElement` is the first child, this function returns a null pointer.
2131/// A returned value should be eventually released using [youtput_destroy] function.
2132#[no_mangle]
2133pub unsafe extern "C" fn yxml_prev_sibling(
2134    xml: *const Branch,
2135    txn: *const Transaction,
2136) -> *mut YOutput {
2137    assert!(!xml.is_null());
2138    assert!(!txn.is_null());
2139
2140    let xml = XmlElementRef::from_raw_branch(xml);
2141    let txn = txn.as_ref().unwrap();
2142
2143    let mut siblings = xml.siblings(txn);
2144    if let Some(next) = siblings.next_back() {
2145        match next {
2146            XmlOut::Element(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlElement(v)))),
2147            XmlOut::Text(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlText(v)))),
2148            XmlOut::Fragment(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlFragment(v)))),
2149        }
2150    } else {
2151        null_mut()
2152    }
2153}
2154
2155/// Returns a parent `YXmlElement` of a current node, or null pointer when current `YXmlElement` is
2156/// a root-level shared data type.
2157#[no_mangle]
2158pub unsafe extern "C" fn yxmlelem_parent(xml: *const Branch) -> *mut Branch {
2159    assert!(!xml.is_null());
2160
2161    let xml = XmlElementRef::from_raw_branch(xml);
2162
2163    if let Some(parent) = xml.parent() {
2164        let branch = parent.as_ptr();
2165        branch.deref() as *const Branch as *mut Branch
2166    } else {
2167        std::ptr::null_mut()
2168    }
2169}
2170
2171/// Returns a number of child nodes (both `YXmlElement` and `YXmlText`) living under a current XML
2172/// element. This function doesn't count a recursive nodes, only direct children of a current node.
2173#[no_mangle]
2174pub unsafe extern "C" fn yxmlelem_child_len(xml: *const Branch, txn: *const Transaction) -> u32 {
2175    assert!(!xml.is_null());
2176    assert!(!txn.is_null());
2177
2178    let txn = txn.as_ref().unwrap();
2179    let xml = XmlElementRef::from_raw_branch(xml);
2180
2181    xml.len(txn) as u32
2182}
2183
2184/// Returns a first child node of a current `YXmlElement`, or null pointer if current XML node is
2185/// empty. Returned value could be either another `YXmlElement` or `YXmlText`.
2186///
2187/// A returned value should be eventually released using [youtput_destroy] function.
2188#[no_mangle]
2189pub unsafe extern "C" fn yxmlelem_first_child(xml: *const Branch) -> *mut YOutput {
2190    assert!(!xml.is_null());
2191
2192    let xml = XmlElementRef::from_raw_branch(xml);
2193
2194    if let Some(value) = xml.first_child() {
2195        match value {
2196            XmlOut::Element(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlElement(v)))),
2197            XmlOut::Text(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlText(v)))),
2198            XmlOut::Fragment(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlFragment(v)))),
2199        }
2200    } else {
2201        std::ptr::null_mut()
2202    }
2203}
2204
2205/// Returns an iterator over a nested recursive structure of a current `YXmlElement`, starting from
2206/// first of its children. Returned values can be either `YXmlElement` or `YXmlText` nodes.
2207///
2208/// Use [yxmlelem_tree_walker_next] function in order to iterate over to a next node.
2209/// Use [yxmlelem_tree_walker_destroy] function to release resources used by the iterator.
2210#[no_mangle]
2211pub unsafe extern "C" fn yxmlelem_tree_walker(
2212    xml: *const Branch,
2213    txn: *const Transaction,
2214) -> *mut TreeWalker {
2215    assert!(!xml.is_null());
2216    assert!(!txn.is_null());
2217
2218    let txn = txn.as_ref().unwrap();
2219    let xml = &XmlElementRef::from_raw_branch(xml) as *const XmlElementRef;
2220    Box::into_raw(Box::new(TreeWalker(xml.as_ref().unwrap().successors(txn))))
2221}
2222
2223/// Releases resources associated with a current XML tree walker iterator.
2224#[no_mangle]
2225pub unsafe extern "C" fn yxmlelem_tree_walker_destroy(iter: *mut TreeWalker) {
2226    if !iter.is_null() {
2227        drop(Box::from_raw(iter))
2228    }
2229}
2230
2231/// Moves current `iterator` to a next value (either `YXmlElement` or `YXmlText`), returning its
2232/// pointer or a null, if an `iterator` already reached the last successor node.
2233///
2234/// Values returned by this function should be eventually released using [youtput_destroy].
2235#[no_mangle]
2236pub unsafe extern "C" fn yxmlelem_tree_walker_next(iterator: *mut TreeWalker) -> *mut YOutput {
2237    assert!(!iterator.is_null());
2238
2239    let iter = iterator.as_mut().unwrap();
2240
2241    if let Some(next) = iter.0.next() {
2242        match next {
2243            XmlOut::Element(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlElement(v)))),
2244            XmlOut::Text(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlText(v)))),
2245            XmlOut::Fragment(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlFragment(v)))),
2246        }
2247    } else {
2248        std::ptr::null_mut()
2249    }
2250}
2251
2252/// Inserts an `YXmlElement` as a child of a current node at the given `index` and returns its
2253/// pointer. Node created this way will have a given `name` as its tag (eg. `p` for `<p></p>` node).
2254///
2255/// An `index` value must be between 0 and (inclusive) length of a current XML element (use
2256/// [yxmlelem_child_len] function to determine its length).
2257///
2258/// A `name` must be a null-terminated UTF-8 encoded string, which will be copied into current
2259/// document. Therefore `name` should be freed by the function caller.
2260#[no_mangle]
2261pub unsafe extern "C" fn yxmlelem_insert_elem(
2262    xml: *const Branch,
2263    txn: *mut Transaction,
2264    index: u32,
2265    name: *const c_char,
2266) -> *mut Branch {
2267    assert!(!xml.is_null());
2268    assert!(!txn.is_null());
2269    assert!(!name.is_null());
2270
2271    let xml = XmlElementRef::from_raw_branch(xml);
2272    let txn = txn.as_mut().unwrap();
2273    let txn = txn
2274        .as_mut()
2275        .expect("provided transaction was not writeable");
2276
2277    let name = CStr::from_ptr(name).to_str().unwrap();
2278    xml.insert(txn, index as u32, XmlElementPrelim::empty(name))
2279        .into_raw_branch()
2280}
2281
2282/// Inserts an `YXmlText` as a child of a current node at the given `index` and returns its
2283/// pointer.
2284///
2285/// An `index` value must be between 0 and (inclusive) length of a current XML element (use
2286/// [yxmlelem_child_len] function to determine its length).
2287#[no_mangle]
2288pub unsafe extern "C" fn yxmlelem_insert_text(
2289    xml: *const Branch,
2290    txn: *mut Transaction,
2291    index: u32,
2292) -> *mut Branch {
2293    assert!(!xml.is_null());
2294    assert!(!txn.is_null());
2295
2296    let xml = XmlElementRef::from_raw_branch(xml);
2297    let txn = txn.as_mut().unwrap();
2298    let txn = txn
2299        .as_mut()
2300        .expect("provided transaction was not writeable");
2301    xml.insert(txn, index as u32, XmlTextPrelim::new(""))
2302        .into_raw_branch()
2303}
2304
2305/// Removes a consecutive range of child elements (of specified length) from the current
2306/// `YXmlElement`, starting at the given `index`. Specified range must fit into boundaries of current
2307/// XML node children, otherwise this function will panic at runtime.
2308#[no_mangle]
2309pub unsafe extern "C" fn yxmlelem_remove_range(
2310    xml: *const Branch,
2311    txn: *mut Transaction,
2312    index: u32,
2313    len: u32,
2314) {
2315    assert!(!xml.is_null());
2316    assert!(!txn.is_null());
2317
2318    let xml = XmlElementRef::from_raw_branch(xml);
2319    let txn = txn.as_mut().unwrap();
2320    let txn = txn
2321        .as_mut()
2322        .expect("provided transaction was not writeable");
2323
2324    xml.remove_range(txn, index as u32, len as u32)
2325}
2326
2327/// Returns an XML child node (either a `YXmlElement` or `YXmlText`) stored at a given `index` of
2328/// a current `YXmlElement`. Returns null pointer if `index` was outside of the bound of current XML
2329/// node children.
2330///
2331/// Returned value should be eventually released using [youtput_destroy].
2332#[no_mangle]
2333pub unsafe extern "C" fn yxmlelem_get(
2334    xml: *const Branch,
2335    txn: *const Transaction,
2336    index: u32,
2337) -> *const YOutput {
2338    assert!(!xml.is_null());
2339    assert!(!txn.is_null());
2340
2341    let xml = XmlElementRef::from_raw_branch(xml);
2342    let txn = txn.as_ref().unwrap();
2343
2344    if let Some(child) = xml.get(txn, index as u32) {
2345        match child {
2346            XmlOut::Element(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlElement(v)))),
2347            XmlOut::Text(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlText(v)))),
2348            XmlOut::Fragment(v) => Box::into_raw(Box::new(YOutput::from(Out::YXmlFragment(v)))),
2349        }
2350    } else {
2351        std::ptr::null()
2352    }
2353}
2354
2355/// Returns the length of the `YXmlText` string content in bytes (without the null terminator
2356/// character)
2357#[no_mangle]
2358pub unsafe extern "C" fn yxmltext_len(txt: *const Branch, txn: *const Transaction) -> u32 {
2359    assert!(!txt.is_null());
2360    assert!(!txn.is_null());
2361
2362    let txn = txn.as_ref().unwrap();
2363    let txt = XmlTextRef::from_raw_branch(txt);
2364
2365    txt.len(txn) as u32
2366}
2367
2368/// Returns a null-terminated UTF-8 encoded string content of a current `YXmlText` shared data type.
2369///
2370/// Generated string resources should be released using [ystring_destroy] function.
2371#[no_mangle]
2372pub unsafe extern "C" fn yxmltext_string(
2373    txt: *const Branch,
2374    txn: *const Transaction,
2375) -> *mut c_char {
2376    assert!(!txt.is_null());
2377    assert!(!txn.is_null());
2378
2379    let txn = txn.as_ref().unwrap();
2380    let txt = XmlTextRef::from_raw_branch(txt);
2381
2382    let str = txt.get_string(txn);
2383    CString::new(str).unwrap().into_raw()
2384}
2385
2386/// Inserts a null-terminated UTF-8 encoded string a a given `index`. `index` value must be between
2387/// 0 and a length of a `YXmlText` (inclusive, accordingly to [yxmltext_len] return value), otherwise
2388/// this function will panic.
2389///
2390/// A `str` parameter must be a null-terminated UTF-8 encoded string. This function doesn't take
2391/// ownership over a passed value - it will be copied and therefore a string parameter must be
2392/// released by the caller.
2393///
2394/// A nullable pointer with defined `attrs` will be used to wrap provided text with
2395/// a formatting blocks. `attrs` must be a map-like type.
2396#[no_mangle]
2397pub unsafe extern "C" fn yxmltext_insert(
2398    txt: *const Branch,
2399    txn: *mut Transaction,
2400    index: u32,
2401    str: *const c_char,
2402    attrs: *const YInput,
2403) {
2404    assert!(!txt.is_null());
2405    assert!(!txn.is_null());
2406    assert!(!str.is_null());
2407
2408    let txt = XmlTextRef::from_raw_branch(txt);
2409    let txn = txn.as_mut().unwrap();
2410    let txn = txn
2411        .as_mut()
2412        .expect("provided transaction was not writeable");
2413    let chunk = CStr::from_ptr(str).to_str().unwrap();
2414
2415    if attrs.is_null() {
2416        txt.insert(txn, index as u32, chunk)
2417    } else {
2418        if let Some(attrs) = map_attrs(attrs.read().into()) {
2419            txt.insert_with_attributes(txn, index as u32, chunk, attrs)
2420        } else {
2421            panic!("yxmltext_insert: passed attributes are not of map type")
2422        }
2423    }
2424}
2425
2426/// Inserts an embed content given `index`. `index` value must be between 0 and a length of a
2427/// `YXmlText` (inclusive, accordingly to [ytext_len] return value), otherwise this
2428/// function will panic.
2429///
2430/// A `str` parameter must be a null-terminated UTF-8 encoded string. This function doesn't take
2431/// ownership over a passed value - it will be copied and therefore a string parameter must be
2432/// released by the caller.
2433///
2434/// A nullable pointer with defined `attrs` will be used to wrap provided text with
2435/// a formatting blocks. `attrs` must be a map-like type.
2436#[no_mangle]
2437pub unsafe extern "C" fn yxmltext_insert_embed(
2438    txt: *const Branch,
2439    txn: *mut Transaction,
2440    index: u32,
2441    content: *const YInput,
2442    attrs: *const YInput,
2443) {
2444    assert!(!txt.is_null());
2445    assert!(!txn.is_null());
2446    assert!(!content.is_null());
2447
2448    let txn = txn.as_mut().unwrap();
2449    let txn = txn
2450        .as_mut()
2451        .expect("provided transaction was not writeable");
2452    let txt = XmlTextRef::from_raw_branch(txt);
2453    let index = index as u32;
2454    let content = content.read();
2455    if attrs.is_null() {
2456        txt.insert_embed(txn, index, content);
2457    } else {
2458        if let Some(attrs) = map_attrs(attrs.read().into()) {
2459            txt.insert_embed_with_attributes(txn, index, content, attrs);
2460        } else {
2461            panic!("yxmltext_insert_embed: passed attributes are not of map type")
2462        }
2463    }
2464}
2465
2466/// Wraps an existing piece of text within a range described by `index`-`len` parameters with
2467/// formatting blocks containing provided `attrs` metadata. `attrs` must be a map-like type.
2468#[no_mangle]
2469pub unsafe extern "C" fn yxmltext_format(
2470    txt: *const Branch,
2471    txn: *mut Transaction,
2472    index: u32,
2473    len: u32,
2474    attrs: *const YInput,
2475) {
2476    assert!(!txt.is_null());
2477    assert!(!txn.is_null());
2478    assert!(!attrs.is_null());
2479
2480    if let Some(attrs) = map_attrs(attrs.read().into()) {
2481        let txt = XmlTextRef::from_raw_branch(txt);
2482        let txn = txn.as_mut().unwrap();
2483        let txn = txn
2484            .as_mut()
2485            .expect("provided transaction was not writeable");
2486        let index = index as u32;
2487        let len = len as u32;
2488        txt.format(txn, index, len, attrs);
2489    } else {
2490        panic!("yxmltext_format: passed attributes are not of map type")
2491    }
2492}
2493
2494/// Removes a range of characters, starting a a given `index`. This range must fit within the bounds
2495/// of a current `YXmlText`, otherwise this function call will fail.
2496///
2497/// An `index` value must be between 0 and the length of a `YXmlText` (exclusive, accordingly to
2498/// [yxmltext_len] return value).
2499///
2500/// A `length` must be lower or equal number of characters (counted as UTF chars depending on the
2501/// encoding configured by `YDoc`) from `index` position to the end of of the string.
2502#[no_mangle]
2503pub unsafe extern "C" fn yxmltext_remove_range(
2504    txt: *const Branch,
2505    txn: *mut Transaction,
2506    idx: u32,
2507    len: u32,
2508) {
2509    assert!(!txt.is_null());
2510    assert!(!txn.is_null());
2511
2512    let txt = XmlTextRef::from_raw_branch(txt);
2513    let txn = txn.as_mut().unwrap();
2514    let txn = txn
2515        .as_mut()
2516        .expect("provided transaction was not writeable");
2517    txt.remove_range(txn, idx as u32, len as u32)
2518}
2519
2520/// Inserts an XML attribute described using `attr_name` and `attr_value`. If another attribute with
2521/// the same name already existed, its value will be replaced with a provided one.
2522///
2523/// Both `attr_name` and `attr_value` must be a null-terminated UTF-8 encoded strings. Their
2524/// contents are being copied, therefore it's up to a function caller to properly release them.
2525#[no_mangle]
2526pub unsafe extern "C" fn yxmltext_insert_attr(
2527    txt: *const Branch,
2528    txn: *mut Transaction,
2529    attr_name: *const c_char,
2530    attr_value: *const YInput,
2531) {
2532    assert!(!txt.is_null());
2533    assert!(!txn.is_null());
2534    assert!(!attr_name.is_null());
2535    assert!(!attr_value.is_null());
2536
2537    let txt = XmlTextRef::from_raw_branch(txt);
2538    let txn = txn.as_mut().unwrap();
2539    let txn = txn
2540        .as_mut()
2541        .expect("provided transaction was not writeable");
2542
2543    let name = CStr::from_ptr(attr_name).to_str().unwrap();
2544
2545    txt.insert_attribute(txn, name, attr_value.read());
2546}
2547
2548/// Removes an attribute from a current `YXmlText`, given its name.
2549///
2550/// An `attr_name`must be a null-terminated UTF-8 encoded string.
2551#[no_mangle]
2552pub unsafe extern "C" fn yxmltext_remove_attr(
2553    txt: *const Branch,
2554    txn: *mut Transaction,
2555    attr_name: *const c_char,
2556) {
2557    assert!(!txt.is_null());
2558    assert!(!txn.is_null());
2559    assert!(!attr_name.is_null());
2560
2561    let txt = XmlTextRef::from_raw_branch(txt);
2562    let txn = txn.as_mut().unwrap();
2563    let txn = txn
2564        .as_mut()
2565        .expect("provided transaction was not writeable");
2566    let name = CStr::from_ptr(attr_name).to_str().unwrap();
2567
2568    txt.remove_attribute(txn, &name)
2569}
2570
2571/// Returns the value of a current `YXmlText`, given its name, or a null pointer if not attribute
2572/// with such name has been found. Returned pointer is a null-terminated UTF-8 encoded string, which
2573/// should be released using [ystring_destroy] function.
2574///
2575/// An `attr_name` must be a null-terminated UTF-8 encoded string.
2576#[no_mangle]
2577pub unsafe extern "C" fn yxmltext_get_attr(
2578    txt: *const Branch,
2579    txn: *const Transaction,
2580    attr_name: *const c_char,
2581) -> *mut YOutput {
2582    assert!(!txt.is_null());
2583    assert!(!attr_name.is_null());
2584    assert!(!txn.is_null());
2585
2586    let txn = txn.as_ref().unwrap();
2587    let txt = XmlTextRef::from_raw_branch(txt);
2588    let name = CStr::from_ptr(attr_name).to_str().unwrap();
2589
2590    if let Some(value) = txt.get_attribute(txn, name) {
2591        let output = YOutput::from(value);
2592        Box::into_raw(Box::new(output))
2593    } else {
2594        std::ptr::null_mut()
2595    }
2596}
2597
2598/// Returns a collection of chunks representing pieces of `YText` rich text string grouped together
2599/// by the same formatting rules and type. `chunks_len` is used to inform about a number of chunks
2600/// generated this way.
2601///
2602/// Returned array needs to be eventually deallocated using `ychunks_destroy`.
2603#[no_mangle]
2604pub unsafe extern "C" fn ytext_chunks(
2605    txt: *const Branch,
2606    txn: *const Transaction,
2607    chunks_len: *mut u32,
2608) -> *mut YChunk {
2609    assert!(!txt.is_null());
2610    assert!(!txn.is_null());
2611
2612    let txt = TextRef::from_raw_branch(txt);
2613    let txn = txn.as_ref().unwrap();
2614
2615    let diffs = txt.diff(txn, YChange::identity);
2616    let chunks: Vec<_> = diffs.into_iter().map(YChunk::from).collect();
2617    let out = chunks.into_boxed_slice();
2618    *chunks_len = out.len() as u32;
2619    Box::into_raw(out) as *mut _
2620}
2621
2622/// Deallocates result of `ytext_chunks` method.
2623#[no_mangle]
2624pub unsafe extern "C" fn ychunks_destroy(chunks: *mut YChunk, len: u32) {
2625    drop(Vec::from_raw_parts(chunks, len as usize, len as usize));
2626}
2627
2628pub const YCHANGE_ADD: i8 = 1;
2629pub const YCHANGE_RETAIN: i8 = 0;
2630pub const YCHANGE_REMOVE: i8 = -1;
2631
2632/// A chunk of text contents formatted with the same set of attributes.
2633#[repr(C)]
2634pub struct YChunk {
2635    /// Piece of YText formatted using the same `fmt` rules. It can be a string, embedded object
2636    /// or another y-type.
2637    pub data: YOutput,
2638    /// Number of formatting attributes attached to current chunk of text.
2639    pub fmt_len: u32,
2640    /// The formatting attributes attached to the current chunk of text.
2641    pub fmt: *mut YMapEntry,
2642}
2643
2644impl From<Diff<YChange>> for YChunk {
2645    fn from(diff: Diff<YChange>) -> Self {
2646        let data = YOutput::from(diff.insert);
2647        let mut fmt_len = 0;
2648        let fmt = if let Some(attrs) = diff.attributes {
2649            fmt_len = attrs.len() as u32;
2650            let mut fmt = Vec::with_capacity(attrs.len());
2651            for (k, v) in attrs.into_iter() {
2652                let output = YOutput::from(&v); //TODO: test if we don't drop memory here
2653                let e = YMapEntry::new(k.as_ref(), Box::new(output));
2654                fmt.push(e);
2655            }
2656            Box::into_raw(fmt.into_boxed_slice()) as *mut _
2657        } else {
2658            null_mut()
2659        };
2660        YChunk { data, fmt_len, fmt }
2661    }
2662}
2663
2664impl Drop for YChunk {
2665    fn drop(&mut self) {
2666        if !self.fmt.is_null() {
2667            drop(unsafe {
2668                Vec::from_raw_parts(self.fmt, self.fmt_len as usize, self.fmt_len as usize)
2669            });
2670        }
2671    }
2672}
2673
2674/// A data structure that is used to pass input values of various types supported by Yrs into a
2675/// shared document store.
2676///
2677/// `YInput` constructor function don't allocate any resources on their own, neither they take
2678/// ownership by pointers to memory blocks allocated by user - for this reason once an input cell
2679/// has been used, its content should be freed by the caller.
2680#[repr(C)]
2681pub struct YInput {
2682    /// Tag describing, which `value` type is being stored by this input cell. Can be one of:
2683    ///
2684    /// - [Y_JSON] for a UTF-8 encoded, NULL-terminated JSON string.
2685    /// - [Y_JSON_BOOL] for boolean flags.
2686    /// - [Y_JSON_NUM] for 64-bit floating point numbers.
2687    /// - [Y_JSON_INT] for 64-bit signed integers.
2688    /// - [Y_JSON_STR] for null-terminated UTF-8 encoded strings.
2689    /// - [Y_JSON_BUF] for embedded binary data.
2690    /// - [Y_JSON_ARR] for arrays of JSON-like values.
2691    /// - [Y_JSON_MAP] for JSON-like objects build from key-value pairs.
2692    /// - [Y_JSON_NULL] for JSON-like null values.
2693    /// - [Y_JSON_UNDEF] for JSON-like undefined values.
2694    /// - [Y_ARRAY] for cells which contents should be used to initialize a `YArray` shared type.
2695    /// - [Y_MAP] for cells which contents should be used to initialize a `YMap` shared type.
2696    /// - [Y_DOC] for cells which contents should be used to nest a `YDoc` sub-document.
2697    /// - [Y_WEAK_LINK] for cells which contents should be used to nest a `YWeakLink` sub-document.
2698    pub tag: i8,
2699
2700    /// Length of the contents stored by current `YInput` cell.
2701    ///
2702    /// For [Y_JSON_NULL] and [Y_JSON_UNDEF] its equal to `0`.
2703    ///
2704    /// For [Y_JSON_ARR], [Y_JSON_MAP], [Y_ARRAY] and [Y_MAP] it describes a number of passed
2705    /// elements.
2706    ///
2707    /// For other types it's always equal to `1`.
2708    pub len: u32,
2709
2710    /// Union struct which contains a content corresponding to a provided `tag` field.
2711    value: YInputContent,
2712}
2713
2714impl YInput {
2715    fn into(self) -> Any {
2716        let tag = self.tag;
2717        unsafe {
2718            match tag {
2719                Y_JSON_STR => {
2720                    let str = CStr::from_ptr(self.value.str).to_str().unwrap().into();
2721                    Any::String(str)
2722                }
2723                Y_JSON => {
2724                    let json_str = CStr::from_ptr(self.value.str).to_str().unwrap();
2725                    serde_json::from_str(json_str).unwrap()
2726                }
2727                Y_JSON_NULL => Any::Null,
2728                Y_JSON_UNDEF => Any::Undefined,
2729                Y_JSON_INT => Any::BigInt(self.value.integer),
2730                Y_JSON_NUM => Any::Number(self.value.num),
2731                Y_JSON_BOOL => Any::Bool(if self.value.flag == 0 { false } else { true }),
2732                Y_JSON_BUF => Any::from(std::slice::from_raw_parts(
2733                    self.value.buf as *mut u8,
2734                    self.len as usize,
2735                )),
2736                Y_JSON_ARR => {
2737                    let ptr = self.value.values;
2738                    let mut dst: Vec<Any> = Vec::with_capacity(self.len as usize);
2739                    let mut i = 0;
2740                    while i < self.len as isize {
2741                        let value = ptr.offset(i).read();
2742                        let any = value.into();
2743                        dst.push(any);
2744                        i += 1;
2745                    }
2746                    Any::from(dst)
2747                }
2748                Y_JSON_MAP => {
2749                    let mut dst = HashMap::with_capacity(self.len as usize);
2750                    let keys = self.value.map.keys;
2751                    let values = self.value.map.values;
2752                    let mut i = 0;
2753                    while i < self.len as isize {
2754                        let key = CStr::from_ptr(keys.offset(i).read())
2755                            .to_str()
2756                            .unwrap()
2757                            .to_owned();
2758                        let value = values.offset(i).read().into();
2759                        dst.insert(key, value);
2760                        i += 1;
2761                    }
2762                    Any::from(dst)
2763                }
2764                Y_DOC => Any::Undefined,
2765                other => panic!("Cannot convert input - unknown tag: {}", other),
2766            }
2767        }
2768    }
2769}
2770
2771impl Into<EmbedPrelim<YInput>> for YInput {
2772    fn into(self) -> EmbedPrelim<YInput> {
2773        if self.tag <= 0 {
2774            EmbedPrelim::Primitive(self.into())
2775        } else {
2776            EmbedPrelim::Shared(self)
2777        }
2778    }
2779}
2780
2781#[repr(C)]
2782union YInputContent {
2783    flag: u8,
2784    num: f64,
2785    integer: i64,
2786    str: *mut c_char,
2787    buf: *mut c_char,
2788    values: *mut YInput,
2789    map: ManuallyDrop<YMapInputData>,
2790    doc: *mut Doc,
2791    weak: *const Weak,
2792}
2793
2794#[repr(C)]
2795struct YMapInputData {
2796    keys: *mut *mut c_char,
2797    values: *mut YInput,
2798}
2799
2800impl Drop for YInput {
2801    fn drop(&mut self) {}
2802}
2803
2804impl Prelim for YInput {
2805    type Return = Unused;
2806
2807    fn into_content<'doc>(self, _: &mut yrs::TransactionMut<'doc>) -> (ItemContent, Option<Self>) {
2808        unsafe {
2809            if self.tag <= 0 {
2810                (ItemContent::Any(vec![self.into()]), None)
2811            } else if self.tag == Y_DOC {
2812                let doc = self.value.doc.as_ref().unwrap();
2813                (ItemContent::Doc(None, doc.clone()), None)
2814            } else {
2815                let type_ref = match self.tag {
2816                    Y_MAP => TypeRef::Map,
2817                    Y_ARRAY => TypeRef::Array,
2818                    Y_TEXT => TypeRef::Text,
2819                    Y_XML_TEXT => TypeRef::XmlText,
2820                    Y_XML_ELEM => {
2821                        let name: Arc<str> =
2822                            CStr::from_ptr(self.value.str).to_str().unwrap().into();
2823                        TypeRef::XmlElement(name)
2824                    }
2825                    Y_WEAK_LINK => {
2826                        let source = Arc::from_raw(self.value.weak);
2827                        TypeRef::WeakLink(source)
2828                    }
2829                    Y_XML_FRAG => TypeRef::XmlFragment,
2830                    other => panic!("unrecognized YInput tag: {}", other),
2831                };
2832                let inner = Branch::new(type_ref);
2833                (ItemContent::Type(inner), Some(self))
2834            }
2835        }
2836    }
2837
2838    fn integrate(self, txn: &mut yrs::TransactionMut, inner_ref: BranchPtr) {
2839        unsafe {
2840            match self.tag {
2841                Y_MAP => {
2842                    let map = MapRef::from(inner_ref);
2843                    let keys = self.value.map.keys;
2844                    let values = self.value.map.values;
2845                    let mut i = 0;
2846                    while i < self.len as isize {
2847                        let key = CStr::from_ptr(keys.offset(i).read())
2848                            .to_str()
2849                            .unwrap()
2850                            .to_owned();
2851                        let value = values.offset(i).read();
2852                        map.insert(txn, key, value);
2853                        i += 1;
2854                    }
2855                }
2856                Y_ARRAY => {
2857                    let array = ArrayRef::from(inner_ref);
2858                    let ptr = self.value.values;
2859                    let len = self.len as isize;
2860                    let mut i = 0;
2861                    while i < len {
2862                        let value = ptr.offset(i).read();
2863                        array.push_back(txn, value);
2864                        i += 1;
2865                    }
2866                }
2867                Y_TEXT => {
2868                    let text = TextRef::from(inner_ref);
2869                    let init = CStr::from_ptr(self.value.str).to_str().unwrap();
2870                    text.push(txn, init);
2871                }
2872                Y_XML_TEXT => {
2873                    let text = XmlTextRef::from(inner_ref);
2874                    let init = CStr::from_ptr(self.value.str).to_str().unwrap();
2875                    text.push(txn, init);
2876                }
2877                _ => { /* do nothing */ }
2878            }
2879        }
2880    }
2881}
2882
2883/// An output value cell returned from yrs API methods. It describes a various types of data
2884/// supported by yrs shared data types.
2885///
2886/// Since `YOutput` instances are always created by calling the corresponding yrs API functions,
2887/// they eventually should be deallocated using [youtput_destroy] function.
2888#[repr(C)]
2889pub struct YOutput {
2890    /// Tag describing, which `value` type is being stored by this input cell. Can be one of:
2891    ///
2892    /// - [Y_JSON_BOOL] for boolean flags.
2893    /// - [Y_JSON_NUM] for 64-bit floating point numbers.
2894    /// - [Y_JSON_INT] for 64-bit signed integers.
2895    /// - [Y_JSON_STR] for null-terminated UTF-8 encoded strings.
2896    /// - [Y_JSON_BUF] for embedded binary data.
2897    /// - [Y_JSON_ARR] for arrays of JSON-like values.
2898    /// - [Y_JSON_MAP] for JSON-like objects build from key-value pairs.
2899    /// - [Y_JSON_NULL] for JSON-like null values.
2900    /// - [Y_JSON_UNDEF] for JSON-like undefined values.
2901    /// - [Y_TEXT] for pointers to `YText` data types.
2902    /// - [Y_ARRAY] for pointers to `YArray` data types.
2903    /// - [Y_MAP] for pointers to `YMap` data types.
2904    /// - [Y_XML_ELEM] for pointers to `YXmlElement` data types.
2905    /// - [Y_XML_TEXT] for pointers to `YXmlText` data types.
2906    /// - [Y_DOC] for pointers to nested `YDocRef` data types.
2907    pub tag: i8,
2908
2909    /// Length of the contents stored by a current `YOutput` cell.
2910    ///
2911    /// For [Y_JSON_NULL] and [Y_JSON_UNDEF] its equal to `0`.
2912    ///
2913    /// For [Y_JSON_ARR], [Y_JSON_MAP] it describes a number of passed elements.
2914    ///
2915    /// For other types it's always equal to `1`.
2916    pub len: u32,
2917
2918    /// Union struct which contains a content corresponding to a provided `tag` field.
2919    value: YOutputContent,
2920}
2921
2922impl YOutput {
2923    #[inline]
2924    unsafe fn null() -> YOutput {
2925        YOutput {
2926            tag: Y_JSON_NULL,
2927            len: 0,
2928            value: MaybeUninit::uninit().assume_init(),
2929        }
2930    }
2931
2932    #[inline]
2933    unsafe fn undefined() -> YOutput {
2934        YOutput {
2935            tag: Y_JSON_UNDEF,
2936            len: 0,
2937            value: MaybeUninit::uninit().assume_init(),
2938        }
2939    }
2940}
2941
2942impl std::fmt::Display for YOutput {
2943    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2944        let tag = self.tag;
2945        unsafe {
2946            if tag == Y_JSON_INT {
2947                write!(f, "{}", self.value.integer)
2948            } else if tag == Y_JSON_NUM {
2949                write!(f, "{}", self.value.num)
2950            } else if tag == Y_JSON_BOOL {
2951                write!(
2952                    f,
2953                    "{}",
2954                    if self.value.flag == 0 {
2955                        "false"
2956                    } else {
2957                        "true"
2958                    }
2959                )
2960            } else if tag == Y_JSON_UNDEF {
2961                write!(f, "undefined")
2962            } else if tag == Y_JSON_NULL {
2963                write!(f, "null")
2964            } else if tag == Y_JSON_STR {
2965                write!(f, "{}", CString::from_raw(self.value.str).to_str().unwrap())
2966            } else if tag == Y_MAP {
2967                write!(f, "YMap")
2968            } else if tag == Y_ARRAY {
2969                write!(f, "YArray")
2970            } else if tag == Y_JSON_ARR {
2971                write!(f, "[")?;
2972                let slice = std::slice::from_raw_parts(self.value.array, self.len as usize);
2973                for o in slice {
2974                    write!(f, ", {}", o)?;
2975                }
2976                write!(f, "]")
2977            } else if tag == Y_JSON_MAP {
2978                write!(f, "{{")?;
2979                let slice = std::slice::from_raw_parts(self.value.map, self.len as usize);
2980                for e in slice {
2981                    let key = CStr::from_ptr(e.key).to_str().unwrap();
2982                    let value = e.value.as_ref().unwrap();
2983                    write!(f, ", '{}' => {}", key, value)?;
2984                }
2985                write!(f, "}}")
2986            } else if tag == Y_TEXT {
2987                write!(f, "YText")
2988            } else if tag == Y_XML_TEXT {
2989                write!(f, "YXmlText")
2990            } else if tag == Y_XML_ELEM {
2991                write!(f, "YXmlElement",)
2992            } else if tag == Y_JSON_BUF {
2993                write!(f, "YBinary(len: {})", self.len)
2994            } else {
2995                Ok(())
2996            }
2997        }
2998    }
2999}
3000
3001impl Drop for YOutput {
3002    fn drop(&mut self) {
3003        let tag = self.tag;
3004        unsafe {
3005            match tag {
3006                Y_JSON_STR => drop(CString::from_raw(self.value.str)),
3007                Y_JSON_ARR => drop(Vec::from_raw_parts(
3008                    self.value.array,
3009                    self.len as usize,
3010                    self.len as usize,
3011                )),
3012                Y_JSON_MAP => drop(Vec::from_raw_parts(
3013                    self.value.map,
3014                    self.len as usize,
3015                    self.len as usize,
3016                )),
3017                Y_JSON_BUF => drop(Vec::from_raw_parts(
3018                    // while we were using Box<[u8]>, for deallocation this should work
3019                    self.value.buf as *mut u8,
3020                    self.len as usize,
3021                    self.len as usize,
3022                )),
3023                Y_DOC => drop(Box::from_raw(self.value.y_doc)),
3024                _ => { /* ignore */ }
3025            }
3026        }
3027    }
3028}
3029
3030impl From<Out> for YOutput {
3031    fn from(v: Out) -> Self {
3032        match v {
3033            Out::Any(v) => Self::from(v),
3034            Out::YText(v) => Self::from(v),
3035            Out::YArray(v) => Self::from(v),
3036            Out::YMap(v) => Self::from(v),
3037            Out::YXmlElement(v) => Self::from(v),
3038            Out::YXmlFragment(v) => Self::from(v),
3039            Out::YXmlText(v) => Self::from(v),
3040            Out::YDoc(v) => Self::from(v),
3041            Out::YWeakLink(v) => Self::from(v),
3042            Out::UndefinedRef(v) => Self::from(v),
3043        }
3044    }
3045}
3046
3047impl From<bool> for YOutput {
3048    #[inline]
3049    fn from(value: bool) -> Self {
3050        YOutput {
3051            tag: Y_JSON_BOOL,
3052            len: 1,
3053            value: YOutputContent {
3054                flag: if value { Y_TRUE } else { Y_FALSE },
3055            },
3056        }
3057    }
3058}
3059
3060impl From<f64> for YOutput {
3061    #[inline]
3062    fn from(value: f64) -> Self {
3063        YOutput {
3064            tag: Y_JSON_NUM,
3065            len: 1,
3066            value: YOutputContent { num: value },
3067        }
3068    }
3069}
3070
3071impl From<i64> for YOutput {
3072    #[inline]
3073    fn from(value: i64) -> Self {
3074        YOutput {
3075            tag: Y_JSON_INT,
3076            len: 1,
3077            value: YOutputContent { integer: value },
3078        }
3079    }
3080}
3081
3082impl<'a> From<&'a str> for YOutput {
3083    fn from(value: &'a str) -> Self {
3084        YOutput {
3085            tag: Y_JSON_STR,
3086            len: value.len() as u32,
3087            value: YOutputContent {
3088                str: CString::new(value).unwrap().into_raw(),
3089            },
3090        }
3091    }
3092}
3093
3094impl<'a> From<&'a [u8]> for YOutput {
3095    fn from(value: &'a [u8]) -> Self {
3096        let value: Box<[u8]> = value.into();
3097        YOutput {
3098            tag: Y_JSON_BUF,
3099            len: value.len() as u32,
3100            value: YOutputContent {
3101                buf: Box::into_raw(value) as *const u8 as *mut c_char,
3102            },
3103        }
3104    }
3105}
3106
3107impl<'a> From<&'a [Any]> for YOutput {
3108    fn from(values: &'a [Any]) -> Self {
3109        let len = values.len() as u32;
3110        let mut array = Vec::with_capacity(values.len());
3111        for v in values.iter() {
3112            let output = YOutput::from(v);
3113            array.push(output);
3114        }
3115        let ptr = array.as_mut_ptr();
3116        forget(array);
3117        YOutput {
3118            tag: Y_JSON_ARR,
3119            len,
3120            value: YOutputContent { array: ptr },
3121        }
3122    }
3123}
3124
3125impl<'a> From<&'a HashMap<String, Any>> for YOutput {
3126    fn from(value: &'a HashMap<String, Any>) -> Self {
3127        let len = value.len() as u32;
3128        let mut array = Vec::with_capacity(len as usize);
3129        for (k, v) in value.iter() {
3130            let entry = YMapEntry::new(k.as_str(), Box::new(YOutput::from(v)));
3131            array.push(entry);
3132        }
3133        let ptr = array.as_mut_ptr();
3134        forget(array);
3135        YOutput {
3136            tag: Y_JSON_MAP,
3137            len,
3138            value: YOutputContent { map: ptr },
3139        }
3140    }
3141}
3142
3143impl<'a> From<&'a Any> for YOutput {
3144    fn from(v: &'a Any) -> Self {
3145        unsafe {
3146            match v {
3147                Any::Null => YOutput::null(),
3148                Any::Undefined => YOutput::undefined(),
3149                Any::Bool(v) => YOutput::from(*v),
3150                Any::Number(v) => YOutput::from(*v),
3151                Any::BigInt(v) => YOutput::from(*v),
3152                Any::String(v) => YOutput::from(v.as_ref()),
3153                Any::Buffer(v) => YOutput::from(v.as_ref()),
3154                Any::Array(v) => YOutput::from(v.as_ref()),
3155                Any::Map(v) => YOutput::from(v.as_ref()),
3156            }
3157        }
3158    }
3159}
3160
3161impl From<Any> for YOutput {
3162    fn from(v: Any) -> Self {
3163        unsafe {
3164            match v {
3165                Any::Null => YOutput::null(),
3166                Any::Undefined => YOutput::undefined(),
3167                Any::Bool(v) => YOutput::from(v),
3168                Any::Number(v) => YOutput::from(v),
3169                Any::BigInt(v) => YOutput::from(v),
3170                Any::String(v) => YOutput::from(v.as_ref()),
3171                Any::Buffer(v) => YOutput::from(v.as_ref()),
3172                Any::Array(v) => YOutput::from(v.as_ref()),
3173                Any::Map(v) => YOutput::from(v.as_ref()),
3174            }
3175        }
3176    }
3177}
3178
3179impl From<TextRef> for YOutput {
3180    fn from(v: TextRef) -> Self {
3181        YOutput {
3182            tag: Y_TEXT,
3183            len: 1,
3184            value: YOutputContent {
3185                y_type: v.into_raw_branch(),
3186            },
3187        }
3188    }
3189}
3190
3191impl From<ArrayRef> for YOutput {
3192    fn from(v: ArrayRef) -> Self {
3193        YOutput {
3194            tag: Y_ARRAY,
3195            len: 1,
3196            value: YOutputContent {
3197                y_type: v.into_raw_branch(),
3198            },
3199        }
3200    }
3201}
3202
3203impl From<WeakRef<BranchPtr>> for YOutput {
3204    fn from(v: WeakRef<BranchPtr>) -> Self {
3205        YOutput {
3206            tag: Y_WEAK_LINK,
3207            len: 1,
3208            value: YOutputContent {
3209                y_type: v.into_raw_branch(),
3210            },
3211        }
3212    }
3213}
3214
3215impl From<MapRef> for YOutput {
3216    fn from(v: MapRef) -> Self {
3217        YOutput {
3218            tag: Y_MAP,
3219            len: 1,
3220            value: YOutputContent {
3221                y_type: v.into_raw_branch(),
3222            },
3223        }
3224    }
3225}
3226
3227impl From<BranchPtr> for YOutput {
3228    fn from(v: BranchPtr) -> Self {
3229        let branch_ref = v.as_ref();
3230        YOutput {
3231            tag: Y_UNDEFINED,
3232            len: 1,
3233            value: YOutputContent {
3234                y_type: branch_ref as *const Branch as *mut Branch,
3235            },
3236        }
3237    }
3238}
3239
3240impl From<XmlElementRef> for YOutput {
3241    fn from(v: XmlElementRef) -> Self {
3242        YOutput {
3243            tag: Y_XML_ELEM,
3244            len: 1,
3245            value: YOutputContent {
3246                y_type: v.into_raw_branch(),
3247            },
3248        }
3249    }
3250}
3251
3252impl From<XmlTextRef> for YOutput {
3253    fn from(v: XmlTextRef) -> Self {
3254        YOutput {
3255            tag: Y_XML_TEXT,
3256            len: 1,
3257            value: YOutputContent {
3258                y_type: v.into_raw_branch(),
3259            },
3260        }
3261    }
3262}
3263
3264impl From<XmlFragmentRef> for YOutput {
3265    fn from(v: XmlFragmentRef) -> Self {
3266        YOutput {
3267            tag: Y_XML_FRAG,
3268            len: 1,
3269            value: YOutputContent {
3270                y_type: v.into_raw_branch(),
3271            },
3272        }
3273    }
3274}
3275
3276impl From<Doc> for YOutput {
3277    fn from(v: Doc) -> Self {
3278        YOutput {
3279            tag: Y_DOC,
3280            len: 1,
3281            value: YOutputContent {
3282                y_doc: Box::into_raw(Box::new(v.clone())),
3283            },
3284        }
3285    }
3286}
3287
3288#[repr(C)]
3289union YOutputContent {
3290    flag: u8,
3291    num: f64,
3292    integer: i64,
3293    str: *mut c_char,
3294    buf: *const c_char,
3295    array: *mut YOutput,
3296    map: *mut YMapEntry,
3297    y_type: *mut Branch,
3298    y_doc: *mut Doc,
3299}
3300
3301/// Releases all resources related to a corresponding `YOutput` cell.
3302#[no_mangle]
3303pub unsafe extern "C" fn youtput_destroy(val: *mut YOutput) {
3304    if !val.is_null() {
3305        drop(Box::from_raw(val))
3306    }
3307}
3308
3309/// Function constructor used to create JSON-like NULL `YInput` cell.
3310/// This function doesn't allocate any heap resources.
3311#[no_mangle]
3312pub unsafe extern "C" fn yinput_null() -> YInput {
3313    YInput {
3314        tag: Y_JSON_NULL,
3315        len: 0,
3316        value: MaybeUninit::uninit().assume_init(),
3317    }
3318}
3319
3320/// Function constructor used to create JSON-like undefined `YInput` cell.
3321/// This function doesn't allocate any heap resources.
3322#[no_mangle]
3323pub unsafe extern "C" fn yinput_undefined() -> YInput {
3324    YInput {
3325        tag: Y_JSON_UNDEF,
3326        len: 0,
3327        value: MaybeUninit::uninit().assume_init(),
3328    }
3329}
3330
3331/// Function constructor used to create JSON-like boolean `YInput` cell.
3332/// This function doesn't allocate any heap resources.
3333#[no_mangle]
3334pub unsafe extern "C" fn yinput_bool(flag: u8) -> YInput {
3335    YInput {
3336        tag: Y_JSON_BOOL,
3337        len: 1,
3338        value: YInputContent { flag },
3339    }
3340}
3341
3342/// Function constructor used to create JSON-like 64-bit floating point number `YInput` cell.
3343/// This function doesn't allocate any heap resources.
3344#[no_mangle]
3345pub unsafe extern "C" fn yinput_float(num: f64) -> YInput {
3346    YInput {
3347        tag: Y_JSON_NUM,
3348        len: 1,
3349        value: YInputContent { num },
3350    }
3351}
3352
3353/// Function constructor used to create JSON-like 64-bit signed integer `YInput` cell.
3354/// This function doesn't allocate any heap resources.
3355#[no_mangle]
3356pub unsafe extern "C" fn yinput_long(integer: i64) -> YInput {
3357    YInput {
3358        tag: Y_JSON_INT,
3359        len: 1,
3360        value: YInputContent { integer },
3361    }
3362}
3363
3364/// Function constructor used to create a string `YInput` cell. Provided parameter must be
3365/// a null-terminated UTF-8 encoded string. This function doesn't allocate any heap resources,
3366/// and doesn't release any on its own, therefore its up to a caller to free resources once
3367/// a structure is no longer needed.
3368#[no_mangle]
3369pub unsafe extern "C" fn yinput_string(str: *const c_char) -> YInput {
3370    YInput {
3371        tag: Y_JSON_STR,
3372        len: 1,
3373        value: YInputContent {
3374            str: str as *mut c_char,
3375        },
3376    }
3377}
3378
3379/// Function constructor used to create aa `YInput` cell representing any JSON-like object.
3380/// Provided parameter must be a null-terminated UTF-8 encoded JSON string.
3381///
3382/// This function doesn't allocate any heap resources and doesn't release any on its own, therefore
3383/// its up to a caller to free resources once a structure is no longer needed.
3384#[no_mangle]
3385pub unsafe extern "C" fn yinput_json(str: *const c_char) -> YInput {
3386    YInput {
3387        tag: Y_JSON,
3388        len: 1,
3389        value: YInputContent {
3390            str: str as *mut c_char,
3391        },
3392    }
3393}
3394
3395/// Function constructor used to create a binary `YInput` cell of a specified length.
3396/// This function doesn't allocate any heap resources and doesn't release any on its own, therefore
3397/// its up to a caller to free resources once a structure is no longer needed.
3398#[no_mangle]
3399pub unsafe extern "C" fn yinput_binary(buf: *const c_char, len: u32) -> YInput {
3400    YInput {
3401        tag: Y_JSON_BUF,
3402        len,
3403        value: YInputContent {
3404            buf: buf as *mut c_char,
3405        },
3406    }
3407}
3408
3409/// Function constructor used to create a JSON-like array `YInput` cell of other JSON-like values of
3410/// a given length. This function doesn't allocate any heap resources and doesn't release any on its
3411/// own, therefore its up to a caller to free resources once a structure is no longer needed.
3412#[no_mangle]
3413pub unsafe extern "C" fn yinput_json_array(values: *mut YInput, len: u32) -> YInput {
3414    YInput {
3415        tag: Y_JSON_ARR,
3416        len,
3417        value: YInputContent { values },
3418    }
3419}
3420
3421/// Function constructor used to create a JSON-like map `YInput` cell of other JSON-like key-value
3422/// pairs. These pairs are build from corresponding indexes of `keys` and `values`, which must have
3423/// the same specified length.
3424///
3425/// This function doesn't allocate any heap resources and doesn't release any on its own, therefore
3426/// its up to a caller to free resources once a structure is no longer needed.
3427#[no_mangle]
3428pub unsafe extern "C" fn yinput_json_map(
3429    keys: *mut *mut c_char,
3430    values: *mut YInput,
3431    len: u32,
3432) -> YInput {
3433    YInput {
3434        tag: Y_JSON_MAP,
3435        len,
3436        value: YInputContent {
3437            map: ManuallyDrop::new(YMapInputData { keys, values }),
3438        },
3439    }
3440}
3441
3442/// Function constructor used to create a nested `YArray` `YInput` cell prefilled with other
3443/// values of a given length. This function doesn't allocate any heap resources and doesn't release
3444/// any on its own, therefore its up to a caller to free resources once a structure is no longer
3445/// needed.
3446#[no_mangle]
3447pub unsafe extern "C" fn yinput_yarray(values: *mut YInput, len: u32) -> YInput {
3448    YInput {
3449        tag: Y_ARRAY,
3450        len,
3451        value: YInputContent { values },
3452    }
3453}
3454
3455/// Function constructor used to create a nested `YMap` `YInput` cell prefilled with other key-value
3456/// pairs. These pairs are build from corresponding indexes of `keys` and `values`, which must have
3457/// the same specified length.
3458///
3459/// This function doesn't allocate any heap resources and doesn't release any on its own, therefore
3460/// its up to a caller to free resources once a structure is no longer needed.
3461#[no_mangle]
3462pub unsafe extern "C" fn yinput_ymap(
3463    keys: *mut *mut c_char,
3464    values: *mut YInput,
3465    len: u32,
3466) -> YInput {
3467    YInput {
3468        tag: Y_MAP,
3469        len,
3470        value: YInputContent {
3471            map: ManuallyDrop::new(YMapInputData { keys, values }),
3472        },
3473    }
3474}
3475
3476/// Function constructor used to create a nested `YText` `YInput` cell prefilled with a specified
3477/// string, which must be a null-terminated UTF-8 character pointer.
3478///
3479/// This function doesn't allocate any heap resources and doesn't release any on its own, therefore
3480/// its up to a caller to free resources once a structure is no longer needed.
3481#[no_mangle]
3482pub unsafe extern "C" fn yinput_ytext(str: *mut c_char) -> YInput {
3483    YInput {
3484        tag: Y_TEXT,
3485        len: 1,
3486        value: YInputContent { str },
3487    }
3488}
3489
3490/// Function constructor used to create a nested `YXmlElement` `YInput` cell with a specified
3491/// tag name, which must be a null-terminated UTF-8 character pointer.
3492///
3493/// This function doesn't allocate any heap resources and doesn't release any on its own, therefore
3494/// its up to a caller to free resources once a structure is no longer needed.
3495#[no_mangle]
3496pub unsafe extern "C" fn yinput_yxmlelem(name: *mut c_char) -> YInput {
3497    YInput {
3498        tag: Y_XML_ELEM,
3499        len: 1,
3500        value: YInputContent { str: name },
3501    }
3502}
3503
3504/// Function constructor used to create a nested `YXmlText` `YInput` cell prefilled with a specified
3505/// string, which must be a null-terminated UTF-8 character pointer.
3506///
3507/// This function doesn't allocate any heap resources and doesn't release any on its own, therefore
3508/// its up to a caller to free resources once a structure is no longer needed.
3509#[no_mangle]
3510pub unsafe extern "C" fn yinput_yxmltext(str: *mut c_char) -> YInput {
3511    YInput {
3512        tag: Y_XML_TEXT,
3513        len: 1,
3514        value: YInputContent { str },
3515    }
3516}
3517
3518/// Function constructor used to create a nested `YDoc` `YInput` cell.
3519///
3520/// This function doesn't allocate any heap resources and doesn't release any on its own, therefore
3521/// its up to a caller to free resources once a structure is no longer needed.
3522#[no_mangle]
3523pub unsafe extern "C" fn yinput_ydoc(doc: *mut Doc) -> YInput {
3524    YInput {
3525        tag: Y_DOC,
3526        len: 1,
3527        value: YInputContent { doc },
3528    }
3529}
3530
3531/// Function constructor used to create a string `YInput` cell with weak reference to another
3532/// element(s) living inside of the same document.
3533#[no_mangle]
3534pub unsafe extern "C" fn yinput_weak(weak: *const Weak) -> YInput {
3535    YInput {
3536        tag: Y_WEAK_LINK,
3537        len: 1,
3538        value: YInputContent { weak },
3539    }
3540}
3541
3542/// Attempts to read the value for a given `YOutput` pointer as a `YDocRef` reference to a nested
3543/// document.
3544#[no_mangle]
3545pub unsafe extern "C" fn youtput_read_ydoc(val: *const YOutput) -> *mut Doc {
3546    let v = val.as_ref().unwrap();
3547    if v.tag == Y_DOC {
3548        v.value.y_doc
3549    } else {
3550        std::ptr::null_mut()
3551    }
3552}
3553
3554/// Attempts to read the value for a given `YOutput` pointer as a boolean flag, which can be either
3555/// `1` for truthy case and `0` otherwise. Returns a null pointer in case when a value stored under
3556/// current `YOutput` cell is not of a boolean type.
3557#[no_mangle]
3558pub unsafe extern "C" fn youtput_read_bool(val: *const YOutput) -> *const u8 {
3559    let v = val.as_ref().unwrap();
3560    if v.tag == Y_JSON_BOOL {
3561        &v.value.flag
3562    } else {
3563        std::ptr::null()
3564    }
3565}
3566
3567/// Attempts to read the value for a given `YOutput` pointer as a 64-bit floating point number.
3568///
3569/// Returns a null pointer in case when a value stored under current `YOutput` cell
3570/// is not a floating point number.
3571#[no_mangle]
3572pub unsafe extern "C" fn youtput_read_float(val: *const YOutput) -> *const f64 {
3573    let v = val.as_ref().unwrap();
3574    if v.tag == Y_JSON_NUM {
3575        &v.value.num
3576    } else {
3577        std::ptr::null()
3578    }
3579}
3580
3581/// Attempts to read the value for a given `YOutput` pointer as a 64-bit signed integer.
3582///
3583/// Returns a null pointer in case when a value stored under current `YOutput` cell
3584/// is not a signed integer.
3585#[no_mangle]
3586pub unsafe extern "C" fn youtput_read_long(val: *const YOutput) -> *const i64 {
3587    let v = val.as_ref().unwrap();
3588    if v.tag == Y_JSON_INT {
3589        &v.value.integer
3590    } else {
3591        std::ptr::null()
3592    }
3593}
3594
3595/// Attempts to read the value for a given `YOutput` pointer as a null-terminated UTF-8 encoded
3596/// string.
3597///
3598/// Returns a null pointer in case when a value stored under current `YOutput` cell
3599/// is not a string. Underlying string is released automatically as part of [youtput_destroy]
3600/// destructor.
3601#[no_mangle]
3602pub unsafe extern "C" fn youtput_read_string(val: *const YOutput) -> *mut c_char {
3603    let v = val.as_ref().unwrap();
3604    if v.tag == Y_JSON_STR {
3605        v.value.str
3606    } else {
3607        std::ptr::null_mut()
3608    }
3609}
3610
3611/// Attempts to read the value for a given `YOutput` pointer as a binary payload (which length is
3612/// stored within `len` filed of a cell itself).
3613///
3614/// Returns a null pointer in case when a value stored under current `YOutput` cell
3615/// is not a binary type. Underlying binary is released automatically as part of [youtput_destroy]
3616/// destructor.
3617#[no_mangle]
3618pub unsafe extern "C" fn youtput_read_binary(val: *const YOutput) -> *const c_char {
3619    let v = val.as_ref().unwrap();
3620    if v.tag == Y_JSON_BUF {
3621        v.value.buf
3622    } else {
3623        std::ptr::null()
3624    }
3625}
3626
3627/// Attempts to read the value for a given `YOutput` pointer as a JSON-like array of `YOutput`
3628/// values (which length is stored within `len` filed of a cell itself).
3629///
3630/// Returns a null pointer in case when a value stored under current `YOutput` cell
3631/// is not a JSON-like array. Underlying heap resources are released automatically as part of
3632/// [youtput_destroy] destructor.
3633#[no_mangle]
3634pub unsafe extern "C" fn youtput_read_json_array(val: *const YOutput) -> *mut YOutput {
3635    let v = val.as_ref().unwrap();
3636    if v.tag == Y_JSON_ARR {
3637        v.value.array
3638    } else {
3639        std::ptr::null_mut()
3640    }
3641}
3642
3643/// Attempts to read the value for a given `YOutput` pointer as a JSON-like map of key-value entries
3644/// (which length is stored within `len` filed of a cell itself).
3645///
3646/// Returns a null pointer in case when a value stored under current `YOutput` cell
3647/// is not a JSON-like map. Underlying heap resources are released automatically as part of
3648/// [youtput_destroy] destructor.
3649#[no_mangle]
3650pub unsafe extern "C" fn youtput_read_json_map(val: *const YOutput) -> *mut YMapEntry {
3651    let v = val.as_ref().unwrap();
3652    if v.tag == Y_JSON_MAP {
3653        v.value.map
3654    } else {
3655        std::ptr::null_mut()
3656    }
3657}
3658
3659/// Attempts to read the value for a given `YOutput` pointer as an `YArray`.
3660///
3661/// Returns a null pointer in case when a value stored under current `YOutput` cell
3662/// is not an `YArray`. Underlying heap resources are released automatically as part of
3663/// [youtput_destroy] destructor.
3664#[no_mangle]
3665pub unsafe extern "C" fn youtput_read_yarray(val: *const YOutput) -> *mut Branch {
3666    let v = val.as_ref().unwrap();
3667    if v.tag == Y_ARRAY {
3668        v.value.y_type
3669    } else {
3670        std::ptr::null_mut()
3671    }
3672}
3673
3674/// Attempts to read the value for a given `YOutput` pointer as an `YXmlElement`.
3675///
3676/// Returns a null pointer in case when a value stored under current `YOutput` cell
3677/// is not an `YXmlElement`. Underlying heap resources are released automatically as part of
3678/// [youtput_destroy] destructor.
3679#[no_mangle]
3680pub unsafe extern "C" fn youtput_read_yxmlelem(val: *const YOutput) -> *mut Branch {
3681    let v = val.as_ref().unwrap();
3682    if v.tag == Y_XML_ELEM {
3683        v.value.y_type
3684    } else {
3685        std::ptr::null_mut()
3686    }
3687}
3688
3689/// Attempts to read the value for a given `YOutput` pointer as an `YMap`.
3690///
3691/// Returns a null pointer in case when a value stored under current `YOutput` cell
3692/// is not an `YMap`. Underlying heap resources are released automatically as part of
3693/// [youtput_destroy] destructor.
3694#[no_mangle]
3695pub unsafe extern "C" fn youtput_read_ymap(val: *const YOutput) -> *mut Branch {
3696    let v = val.as_ref().unwrap();
3697    if v.tag == Y_MAP {
3698        v.value.y_type
3699    } else {
3700        std::ptr::null_mut()
3701    }
3702}
3703
3704/// Attempts to read the value for a given `YOutput` pointer as an `YText`.
3705///
3706/// Returns a null pointer in case when a value stored under current `YOutput` cell
3707/// is not an `YText`. Underlying heap resources are released automatically as part of
3708/// [youtput_destroy] destructor.
3709#[no_mangle]
3710pub unsafe extern "C" fn youtput_read_ytext(val: *const YOutput) -> *mut Branch {
3711    let v = val.as_ref().unwrap();
3712    if v.tag == Y_TEXT {
3713        v.value.y_type
3714    } else {
3715        std::ptr::null_mut()
3716    }
3717}
3718
3719/// Attempts to read the value for a given `YOutput` pointer as an `YXmlText`.
3720///
3721/// Returns a null pointer in case when a value stored under current `YOutput` cell
3722/// is not an `YXmlText`. Underlying heap resources are released automatically as part of
3723/// [youtput_destroy] destructor.
3724#[no_mangle]
3725pub unsafe extern "C" fn youtput_read_yxmltext(val: *const YOutput) -> *mut Branch {
3726    let v = val.as_ref().unwrap();
3727    if v.tag == Y_XML_TEXT {
3728        v.value.y_type
3729    } else {
3730        std::ptr::null_mut()
3731    }
3732}
3733
3734/// Attempts to read the value for a given `YOutput` pointer as an `YWeakRef`.
3735///
3736/// Returns a null pointer in case when a value stored under current `YOutput` cell
3737/// is not an `YWeakRef`. Underlying heap resources are released automatically as part of
3738/// [youtput_destroy] destructor.
3739#[no_mangle]
3740pub unsafe extern "C" fn youtput_read_yweak(val: *const YOutput) -> *mut Branch {
3741    let v = val.as_ref().unwrap();
3742    if v.tag == Y_WEAK_LINK {
3743        v.value.y_type
3744    } else {
3745        std::ptr::null_mut()
3746    }
3747}
3748
3749/// Unsubscribe callback from the oberver event it was previously subscribed to.
3750#[no_mangle]
3751pub unsafe extern "C" fn yunobserve(subscription: *mut Subscription) {
3752    drop(unsafe { Box::from_raw(subscription) })
3753}
3754
3755/// Subscribes a given callback function `cb` to changes made by this `YText` instance. Callbacks
3756/// are triggered whenever a `ytransaction_commit` is called.
3757/// Returns a subscription ID which can be then used to unsubscribe this callback by using
3758/// `yunobserve` function.
3759#[no_mangle]
3760pub unsafe extern "C" fn ytext_observe(
3761    txt: *const Branch,
3762    state: *mut c_void,
3763    cb: extern "C" fn(*mut c_void, *const YTextEvent),
3764) -> *mut Subscription {
3765    assert!(!txt.is_null());
3766    let state = CallbackState::new(state);
3767
3768    let txt = TextRef::from_raw_branch(txt);
3769    let subscription = txt.observe(move |txn, e| {
3770        let e = YTextEvent::new(e, txn);
3771        cb(state.0, &e as *const YTextEvent);
3772    });
3773    Box::into_raw(Box::new(subscription))
3774}
3775
3776/// Subscribes a given callback function `cb` to changes made by this `YMap` instance. Callbacks
3777/// are triggered whenever a `ytransaction_commit` is called.
3778/// Returns a subscription ID which can be then used to unsubscribe this callback by using
3779/// `yunobserve` function.
3780#[no_mangle]
3781pub unsafe extern "C" fn ymap_observe(
3782    map: *const Branch,
3783    state: *mut c_void,
3784    cb: extern "C" fn(*mut c_void, *const YMapEvent),
3785) -> *mut Subscription {
3786    assert!(!map.is_null());
3787    let state = CallbackState::new(state);
3788
3789    let map = MapRef::from_raw_branch(map);
3790    let subscription = map.observe(move |txn, e| {
3791        let e = YMapEvent::new(e, txn);
3792        cb(state.0, &e as *const YMapEvent);
3793    });
3794    Box::into_raw(Box::new(subscription))
3795}
3796
3797/// Subscribes a given callback function `cb` to changes made by this `YArray` instance. Callbacks
3798/// are triggered whenever a `ytransaction_commit` is called.
3799/// Returns a subscription ID which can be then used to unsubscribe this callback by using
3800/// `yunobserve` function.
3801#[no_mangle]
3802pub unsafe extern "C" fn yarray_observe(
3803    array: *const Branch,
3804    state: *mut c_void,
3805    cb: extern "C" fn(*mut c_void, *const YArrayEvent),
3806) -> *mut Subscription {
3807    assert!(!array.is_null());
3808    let state = CallbackState::new(state);
3809
3810    let array = ArrayRef::from_raw_branch(array);
3811    let subscription = array.observe(move |txn, e| {
3812        let e = YArrayEvent::new(e, txn);
3813        cb(state.0, &e as *const YArrayEvent);
3814    });
3815    Box::into_raw(Box::new(subscription))
3816}
3817
3818/// Subscribes a given callback function `cb` to changes made by this `YXmlElement` instance.
3819/// Callbacks are triggered whenever a `ytransaction_commit` is called.
3820/// Returns a subscription ID which can be then used to unsubscribe this callback by using
3821/// `yunobserve` function.
3822#[no_mangle]
3823pub unsafe extern "C" fn yxmlelem_observe(
3824    xml: *const Branch,
3825    state: *mut c_void,
3826    cb: extern "C" fn(*mut c_void, *const YXmlEvent),
3827) -> *mut Subscription {
3828    assert!(!xml.is_null());
3829    let state = CallbackState::new(state);
3830
3831    let xml = XmlElementRef::from_raw_branch(xml);
3832    let subscription = xml.observe(move |txn, e| {
3833        let e = YXmlEvent::new(e, txn);
3834        cb(state.0, &e as *const YXmlEvent);
3835    });
3836    Box::into_raw(Box::new(subscription))
3837}
3838
3839/// Subscribes a given callback function `cb` to changes made by this `YXmlText` instance. Callbacks
3840/// are triggered whenever a `ytransaction_commit` is called.
3841/// Returns a subscription ID which can be then used to unsubscribe this callback by using
3842/// `yunobserve` function.
3843#[no_mangle]
3844pub unsafe extern "C" fn yxmltext_observe(
3845    xml: *const Branch,
3846    state: *mut c_void,
3847    cb: extern "C" fn(*mut c_void, *const YXmlTextEvent),
3848) -> *mut Subscription {
3849    assert!(!xml.is_null());
3850
3851    let state = CallbackState::new(state);
3852    let xml = XmlTextRef::from_raw_branch(xml);
3853    let subscription = xml.observe(move |txn, e| {
3854        let e = YXmlTextEvent::new(e, txn);
3855        cb(state.0, &e as *const YXmlTextEvent);
3856    });
3857    Box::into_raw(Box::new(subscription))
3858}
3859
3860/// Subscribes a given callback function `cb` to changes made by this shared type instance as well
3861/// as all nested shared types living within it. Callbacks are triggered whenever a
3862/// `ytransaction_commit` is called.
3863///
3864/// Returns a subscription ID which can be then used to unsubscribe this callback by using
3865/// `yunobserve` function.
3866#[no_mangle]
3867pub unsafe extern "C" fn yobserve_deep(
3868    ytype: *mut Branch,
3869    state: *mut c_void,
3870    cb: extern "C" fn(*mut c_void, u32, *const YEvent),
3871) -> *mut Subscription {
3872    assert!(!ytype.is_null());
3873
3874    let state = CallbackState::new(state);
3875    let branch = ytype.as_mut().unwrap();
3876    let subscription = branch.observe_deep(move |txn, events| {
3877        let events: Vec<_> = events.iter().map(|e| YEvent::new(txn, e)).collect();
3878        let len = events.len() as u32;
3879        cb(state.0, len, events.as_ptr());
3880    });
3881    Box::into_raw(Box::new(subscription))
3882}
3883
3884/// Event generated for callbacks subscribed using `ydoc_observe_after_transaction`. It contains
3885/// snapshot of changes made within any committed transaction.
3886#[repr(C)]
3887pub struct YAfterTransactionEvent {
3888    /// Descriptor of a document state at the moment of creating the transaction.
3889    pub before_state: YStateVector,
3890    /// Descriptor of a document state at the moment of committing the transaction.
3891    pub after_state: YStateVector,
3892    /// Information about all items deleted within the scope of a transaction.
3893    pub delete_set: YIdSet,
3894}
3895
3896impl YAfterTransactionEvent {
3897    unsafe fn new(e: &TransactionCleanupEvent) -> Self {
3898        YAfterTransactionEvent {
3899            before_state: YStateVector::new(&e.before_state),
3900            after_state: YStateVector::new(&e.after_state),
3901            delete_set: YIdSet::new(&e.delete_set),
3902        }
3903    }
3904}
3905
3906#[repr(C)]
3907pub struct YSubdocsEvent {
3908    added_len: u32,
3909    removed_len: u32,
3910    loaded_len: u32,
3911    added: *mut *mut Doc,
3912    removed: *mut *mut Doc,
3913    loaded: *mut *mut Doc,
3914}
3915
3916impl YSubdocsEvent {
3917    unsafe fn new(e: &SubdocsEvent) -> Self {
3918        fn into_ptr(v: SubdocsEventIter) -> *mut *mut Doc {
3919            let array: Vec<_> = v.map(|doc| Box::into_raw(Box::new(doc.clone()))).collect();
3920            let mut boxed = array.into_boxed_slice();
3921            let ptr = boxed.as_mut_ptr();
3922            forget(boxed);
3923            ptr
3924        }
3925
3926        let added = e.added();
3927        let removed = e.removed();
3928        let loaded = e.loaded();
3929
3930        YSubdocsEvent {
3931            added_len: added.len() as u32,
3932            removed_len: removed.len() as u32,
3933            loaded_len: loaded.len() as u32,
3934            added: into_ptr(added),
3935            removed: into_ptr(removed),
3936            loaded: into_ptr(loaded),
3937        }
3938    }
3939}
3940
3941impl Drop for YSubdocsEvent {
3942    fn drop(&mut self) {
3943        fn release(len: u32, buf: *mut *mut Doc) {
3944            unsafe {
3945                let docs = Vec::from_raw_parts(buf, len as usize, len as usize);
3946                for d in docs {
3947                    drop(Box::from_raw(d));
3948                }
3949            }
3950        }
3951
3952        release(self.added_len, self.added);
3953        release(self.removed_len, self.removed);
3954        release(self.loaded_len, self.loaded);
3955    }
3956}
3957
3958/// Struct representing a state of a document. It contains the last seen clocks for blocks submitted
3959/// per any of the clients collaborating on document updates.
3960#[repr(C)]
3961pub struct YStateVector {
3962    /// Number of clients. It describes a length of both `client_ids` and `clocks` arrays.
3963    pub entries_count: u32,
3964    /// Array of unique client identifiers (length is given in `entries_count` field). Each client
3965    /// ID has corresponding clock attached, which can be found in `clocks` field under the same
3966    /// index.
3967    pub client_ids: *mut u64,
3968    /// Array of clocks (length is given in `entries_count` field) known for each client. Each clock
3969    /// has a corresponding client identifier attached, which can be found in `client_ids` field
3970    /// under the same index.
3971    pub clocks: *mut u32,
3972}
3973
3974impl YStateVector {
3975    unsafe fn new(sv: &StateVector) -> Self {
3976        let entries_count = sv.len() as u32;
3977        let mut client_ids = Vec::with_capacity(sv.len());
3978        let mut clocks = Vec::with_capacity(sv.len());
3979        for (&client, &clock) in sv.iter() {
3980            client_ids.push(client.get());
3981            clocks.push(clock as u32);
3982        }
3983
3984        YStateVector {
3985            entries_count,
3986            client_ids: Box::into_raw(client_ids.into_boxed_slice()) as *mut _,
3987            clocks: Box::into_raw(clocks.into_boxed_slice()) as *mut _,
3988        }
3989    }
3990}
3991
3992impl Drop for YStateVector {
3993    fn drop(&mut self) {
3994        let len = self.entries_count as usize;
3995        drop(unsafe { Vec::from_raw_parts(self.client_ids, len, len) });
3996        drop(unsafe { Vec::from_raw_parts(self.clocks, len, len) });
3997    }
3998}
3999
4000/// Delete set is a map of `(ClientID, Range[])` entries. Length of a map is stored in
4001/// `entries_count` field. ClientIDs reside under `client_ids` and their corresponding range
4002/// sequences can be found under the same index of `ranges` field.
4003#[repr(C)]
4004pub struct YIdSet {
4005    /// Number of client identifier entries.
4006    pub entries_count: u32,
4007    /// Array of unique client identifiers (length is given in `entries_count` field). Each client
4008    /// ID has corresponding sequence of ranges attached, which can be found in `ranges` field under
4009    /// the same index.
4010    pub client_ids: *mut u64,
4011    /// Array of range sequences (length is given in `entries_count` field). Each sequence has
4012    /// a corresponding client ID attached, which can be found in `client_ids` field under
4013    /// the same index.
4014    pub ranges: *mut YIdRangeSeq,
4015}
4016
4017impl YIdSet {
4018    unsafe fn new(ds: &IdSet) -> Self {
4019        let len = ds.len();
4020        let mut client_ids = Vec::with_capacity(len);
4021        let mut ranges = Vec::with_capacity(len);
4022
4023        for (&client, range) in ds.iter() {
4024            client_ids.push(client.get());
4025            let seq: Vec<_> = range
4026                .iter()
4027                .map(|r| YIdRange {
4028                    start: r.start as u32,
4029                    end: r.end as u32,
4030                })
4031                .collect();
4032            ranges.push(YIdRangeSeq {
4033                len: seq.len() as u32,
4034                seq: Box::into_raw(seq.into_boxed_slice()) as *mut _,
4035            })
4036        }
4037
4038        YIdSet {
4039            entries_count: len as u32,
4040            client_ids: Box::into_raw(client_ids.into_boxed_slice()) as *mut _,
4041            ranges: Box::into_raw(ranges.into_boxed_slice()) as *mut _,
4042        }
4043    }
4044}
4045
4046impl Drop for YIdSet {
4047    fn drop(&mut self) {
4048        let len = self.entries_count as usize;
4049        drop(unsafe { Vec::from_raw_parts(self.client_ids, len, len) });
4050        drop(unsafe { Vec::from_raw_parts(self.ranges, len, len) });
4051    }
4052}
4053
4054/// Fixed-length sequence of ID ranges. Each range is a pair of [start, end) values, describing the
4055/// range of items identified by clock values, that this range refers to.
4056#[repr(C)]
4057pub struct YIdRangeSeq {
4058    /// Number of ranges stored in this sequence.
4059    pub len: u32,
4060    /// Array (length is stored in `len` field) or ranges. Each range is a pair of [start, end)
4061    /// values, describing continuous collection of items produced by the same client, identified
4062    /// by clock values, that this range refers to.
4063    pub seq: *mut YIdRange,
4064}
4065
4066impl Drop for YIdRangeSeq {
4067    fn drop(&mut self) {
4068        let len = self.len as usize;
4069        drop(unsafe { Vec::from_raw_parts(self.seq, len, len) })
4070    }
4071}
4072
4073#[repr(C)]
4074pub struct YIdRange {
4075    pub start: u32,
4076    pub end: u32,
4077}
4078
4079#[repr(C)]
4080pub struct YEvent {
4081    /// Tag describing, which shared type emitted this event.
4082    ///
4083    /// - [Y_TEXT] for pointers to `YText` data types.
4084    /// - [Y_ARRAY] for pointers to `YArray` data types.
4085    /// - [Y_MAP] for pointers to `YMap` data types.
4086    /// - [Y_XML_ELEM] for pointers to `YXmlElement` data types.
4087    /// - [Y_XML_TEXT] for pointers to `YXmlText` data types.
4088    pub tag: i8,
4089
4090    /// A nested event type, specific for a shared data type that triggered it. Type of an
4091    /// event can be verified using `tag` field.
4092    pub content: YEventContent,
4093}
4094
4095impl YEvent {
4096    fn new<'doc>(txn: &yrs::TransactionMut<'doc>, e: &Event) -> YEvent {
4097        match e {
4098            Event::Text(e) => YEvent {
4099                tag: Y_TEXT,
4100                content: YEventContent {
4101                    text: YTextEvent::new(e, txn),
4102                },
4103            },
4104            Event::Array(e) => YEvent {
4105                tag: Y_ARRAY,
4106                content: YEventContent {
4107                    array: YArrayEvent::new(e, txn),
4108                },
4109            },
4110            Event::Map(e) => YEvent {
4111                tag: Y_MAP,
4112                content: YEventContent {
4113                    map: YMapEvent::new(e, txn),
4114                },
4115            },
4116            Event::XmlFragment(e) => YEvent {
4117                tag: if let XmlOut::Fragment(_) = e.target() {
4118                    Y_XML_FRAG
4119                } else {
4120                    Y_XML_ELEM
4121                },
4122                content: YEventContent {
4123                    xml_elem: YXmlEvent::new(e, txn),
4124                },
4125            },
4126            Event::XmlText(e) => YEvent {
4127                tag: Y_XML_TEXT,
4128                content: YEventContent {
4129                    xml_text: YXmlTextEvent::new(e, txn),
4130                },
4131            },
4132            Event::Weak(e) => YEvent {
4133                tag: Y_WEAK_LINK,
4134                content: YEventContent {
4135                    weak: YWeakLinkEvent::new(e, txn),
4136                },
4137            },
4138        }
4139    }
4140}
4141
4142#[repr(C)]
4143pub union YEventContent {
4144    pub text: YTextEvent,
4145    pub map: YMapEvent,
4146    pub array: YArrayEvent,
4147    pub xml_elem: YXmlEvent,
4148    pub xml_text: YXmlTextEvent,
4149    pub weak: YWeakLinkEvent,
4150}
4151
4152/// Event pushed into callbacks registered with `ytext_observe` function. It contains delta of all
4153/// text changes made within a scope of corresponding transaction (see: `ytext_event_delta`) as
4154/// well as navigation data used to identify a `YText` instance which triggered this event.
4155#[repr(C)]
4156#[derive(Copy, Clone)]
4157pub struct YTextEvent {
4158    inner: *const c_void,
4159    txn: *const yrs::TransactionMut<'static>,
4160}
4161
4162impl YTextEvent {
4163    fn new<'dev>(inner: &TextEvent, txn: &yrs::TransactionMut<'dev>) -> Self {
4164        let inner = inner as *const TextEvent as *const _;
4165        let txn: &yrs::TransactionMut<'static> = unsafe { std::mem::transmute(txn) };
4166        let txn = txn as *const _;
4167        YTextEvent { inner, txn }
4168    }
4169
4170    fn txn(&self) -> &yrs::TransactionMut {
4171        unsafe { self.txn.as_ref().unwrap() }
4172    }
4173}
4174
4175impl Deref for YTextEvent {
4176    type Target = TextEvent;
4177
4178    fn deref(&self) -> &Self::Target {
4179        unsafe { (self.inner as *const TextEvent).as_ref().unwrap() }
4180    }
4181}
4182
4183/// Event pushed into callbacks registered with `yarray_observe` function. It contains delta of all
4184/// content changes made within a scope of corresponding transaction (see: `yarray_event_delta`) as
4185/// well as navigation data used to identify a `YArray` instance which triggered this event.
4186#[repr(C)]
4187#[derive(Copy, Clone)]
4188pub struct YArrayEvent {
4189    inner: *const c_void,
4190    txn: *const yrs::TransactionMut<'static>,
4191}
4192
4193impl YArrayEvent {
4194    fn new<'doc>(inner: &ArrayEvent, txn: &yrs::TransactionMut<'doc>) -> Self {
4195        let inner = inner as *const ArrayEvent as *const _;
4196        let txn: &yrs::TransactionMut<'static> = unsafe { std::mem::transmute(txn) };
4197        let txn = txn as *const _;
4198        YArrayEvent { inner, txn }
4199    }
4200
4201    fn txn(&self) -> &yrs::TransactionMut {
4202        unsafe { self.txn.as_ref().unwrap() }
4203    }
4204}
4205
4206impl Deref for YArrayEvent {
4207    type Target = ArrayEvent;
4208
4209    fn deref(&self) -> &Self::Target {
4210        unsafe { (self.inner as *const ArrayEvent).as_ref().unwrap() }
4211    }
4212}
4213
4214/// Event pushed into callbacks registered with `ymap_observe` function. It contains all
4215/// key-value changes made within a scope of corresponding transaction (see: `ymap_event_keys`) as
4216/// well as navigation data used to identify a `YMap` instance which triggered this event.
4217#[repr(C)]
4218#[derive(Copy, Clone)]
4219pub struct YMapEvent {
4220    inner: *const c_void,
4221    txn: *const yrs::TransactionMut<'static>,
4222}
4223
4224impl YMapEvent {
4225    fn new<'doc>(inner: &MapEvent, txn: &yrs::TransactionMut<'doc>) -> Self {
4226        let inner = inner as *const MapEvent as *const _;
4227        let txn: &yrs::TransactionMut<'static> = unsafe { std::mem::transmute(txn) };
4228        let txn = txn as *const _;
4229        YMapEvent { inner, txn }
4230    }
4231
4232    fn txn(&self) -> &yrs::TransactionMut<'static> {
4233        unsafe { self.txn.as_ref().unwrap() }
4234    }
4235}
4236
4237impl Deref for YMapEvent {
4238    type Target = MapEvent;
4239
4240    fn deref(&self) -> &Self::Target {
4241        unsafe { (self.inner as *const MapEvent).as_ref().unwrap() }
4242    }
4243}
4244
4245/// Event pushed into callbacks registered with `yxmlelem_observe` function. It contains
4246/// all attribute changes made within a scope of corresponding transaction
4247/// (see: `yxmlelem_event_keys`) as well as child XML nodes changes (see: `yxmlelem_event_delta`)
4248/// and navigation data used to identify a `YXmlElement` instance which triggered this event.
4249#[repr(C)]
4250#[derive(Copy, Clone)]
4251pub struct YXmlEvent {
4252    inner: *const c_void,
4253    txn: *const yrs::TransactionMut<'static>,
4254}
4255
4256impl YXmlEvent {
4257    fn new<'doc>(inner: &XmlEvent, txn: &yrs::TransactionMut<'doc>) -> Self {
4258        let inner = inner as *const XmlEvent as *const _;
4259        let txn: &yrs::TransactionMut<'static> = unsafe { std::mem::transmute(txn) };
4260        let txn = txn as *const _;
4261        YXmlEvent { inner, txn }
4262    }
4263
4264    fn txn(&self) -> &yrs::TransactionMut<'static> {
4265        unsafe { self.txn.as_ref().unwrap() }
4266    }
4267}
4268
4269impl Deref for YXmlEvent {
4270    type Target = XmlEvent;
4271
4272    fn deref(&self) -> &Self::Target {
4273        unsafe { (self.inner as *const XmlEvent).as_ref().unwrap() }
4274    }
4275}
4276
4277/// Event pushed into callbacks registered with `yxmltext_observe` function. It contains
4278/// all attribute changes made within a scope of corresponding transaction
4279/// (see: `yxmltext_event_keys`) as well as text edits (see: `yxmltext_event_delta`)
4280/// and navigation data used to identify a `YXmlText` instance which triggered this event.
4281#[repr(C)]
4282#[derive(Copy, Clone)]
4283pub struct YXmlTextEvent {
4284    inner: *const c_void,
4285    txn: *const yrs::TransactionMut<'static>,
4286}
4287
4288impl YXmlTextEvent {
4289    fn new<'doc>(inner: &XmlTextEvent, txn: &yrs::TransactionMut<'doc>) -> Self {
4290        let inner = inner as *const XmlTextEvent as *const _;
4291        let txn: &yrs::TransactionMut<'static> = unsafe { std::mem::transmute(txn) };
4292        let txn = txn as *const _;
4293        YXmlTextEvent { inner, txn }
4294    }
4295
4296    fn txn(&self) -> &yrs::TransactionMut<'static> {
4297        unsafe { self.txn.as_ref().unwrap() }
4298    }
4299}
4300
4301impl Deref for YXmlTextEvent {
4302    type Target = XmlTextEvent;
4303
4304    fn deref(&self) -> &Self::Target {
4305        unsafe { (self.inner as *const XmlTextEvent).as_ref().unwrap() }
4306    }
4307}
4308
4309/// Event pushed into callbacks registered with `yweak_observe` function. It contains
4310/// all an event changes of the underlying transaction.
4311#[repr(C)]
4312#[derive(Copy, Clone)]
4313pub struct YWeakLinkEvent {
4314    inner: *const c_void,
4315    txn: *const yrs::TransactionMut<'static>,
4316}
4317
4318impl YWeakLinkEvent {
4319    fn new<'doc>(inner: &WeakEvent, txn: &yrs::TransactionMut<'doc>) -> Self {
4320        let inner = inner as *const WeakEvent as *const _;
4321        let txn: &yrs::TransactionMut<'static> = unsafe { std::mem::transmute(txn) };
4322        let txn = txn as *const _;
4323        YWeakLinkEvent { inner, txn }
4324    }
4325}
4326
4327impl Deref for YWeakLinkEvent {
4328    type Target = WeakEvent;
4329
4330    fn deref(&self) -> &Self::Target {
4331        unsafe { (self.inner as *const WeakEvent).as_ref().unwrap() }
4332    }
4333}
4334
4335/// Returns a pointer to a shared collection, which triggered passed event `e`.
4336#[no_mangle]
4337pub unsafe extern "C" fn ytext_event_target(e: *const YTextEvent) -> *mut Branch {
4338    assert!(!e.is_null());
4339    let out = (&*e).target().clone();
4340    out.into_raw_branch()
4341}
4342
4343/// Returns a pointer to a shared collection, which triggered passed event `e`.
4344#[no_mangle]
4345pub unsafe extern "C" fn yarray_event_target(e: *const YArrayEvent) -> *mut Branch {
4346    assert!(!e.is_null());
4347    let out = (&*e).target().clone();
4348    out.into_raw_branch()
4349}
4350
4351/// Returns a pointer to a shared collection, which triggered passed event `e`.
4352#[no_mangle]
4353pub unsafe extern "C" fn ymap_event_target(e: *const YMapEvent) -> *mut Branch {
4354    assert!(!e.is_null());
4355    let out = (&*e).target().clone();
4356    out.into_raw_branch()
4357}
4358
4359/// Returns a pointer to a shared collection, which triggered passed event `e`.
4360#[no_mangle]
4361pub unsafe extern "C" fn yxmlelem_event_target(e: *const YXmlEvent) -> *mut Branch {
4362    assert!(!e.is_null());
4363    let out = (&*e).target().clone();
4364    match out {
4365        XmlOut::Element(e) => e.into_raw_branch(),
4366        XmlOut::Fragment(e) => e.into_raw_branch(),
4367        XmlOut::Text(e) => e.into_raw_branch(),
4368    }
4369}
4370
4371/// Returns a pointer to a shared collection, which triggered passed event `e`.
4372#[no_mangle]
4373pub unsafe extern "C" fn yxmltext_event_target(e: *const YXmlTextEvent) -> *mut Branch {
4374    assert!(!e.is_null());
4375    let out = (&*e).target().clone();
4376    out.into_raw_branch()
4377}
4378
4379/// Returns a path from a root type down to a current shared collection (which can be obtained using
4380/// `ytext_event_target` function). It can consist of either integer indexes (used by sequence
4381/// components) or *char keys (used by map components). `len` output parameter is used to provide
4382/// information about length of the path.
4383///
4384/// Path returned this way should be eventually released using `ypath_destroy`.
4385#[no_mangle]
4386pub unsafe extern "C" fn ytext_event_path(
4387    e: *const YTextEvent,
4388    len: *mut u32,
4389) -> *mut YPathSegment {
4390    assert!(!e.is_null());
4391    let e = &*e;
4392    let path: Vec<_> = e.path().into_iter().map(YPathSegment::from).collect();
4393    let out = path.into_boxed_slice();
4394    *len = out.len() as u32;
4395    Box::into_raw(out) as *mut _
4396}
4397
4398/// Returns a path from a root type down to a current shared collection (which can be obtained using
4399/// `ymap_event_target` function). It can consist of either integer indexes (used by sequence
4400/// components) or *char keys (used by map components). `len` output parameter is used to provide
4401/// information about length of the path.
4402///
4403/// Path returned this way should be eventually released using `ypath_destroy`.
4404#[no_mangle]
4405pub unsafe extern "C" fn ymap_event_path(e: *const YMapEvent, len: *mut u32) -> *mut YPathSegment {
4406    assert!(!e.is_null());
4407    let e = &*e;
4408    let path: Vec<_> = e.path().into_iter().map(YPathSegment::from).collect();
4409    let out = path.into_boxed_slice();
4410    *len = out.len() as u32;
4411    Box::into_raw(out) as *mut _
4412}
4413
4414/// Returns a path from a root type down to a current shared collection (which can be obtained using
4415/// `yxmlelem_event_path` function). It can consist of either integer indexes (used by sequence
4416/// components) or *char keys (used by map components). `len` output parameter is used to provide
4417/// information about length of the path.
4418///
4419/// Path returned this way should be eventually released using `ypath_destroy`.
4420#[no_mangle]
4421pub unsafe extern "C" fn yxmlelem_event_path(
4422    e: *const YXmlEvent,
4423    len: *mut u32,
4424) -> *mut YPathSegment {
4425    assert!(!e.is_null());
4426    let e = &*e;
4427    let path: Vec<_> = e.path().into_iter().map(YPathSegment::from).collect();
4428    let out = path.into_boxed_slice();
4429    *len = out.len() as u32;
4430    Box::into_raw(out) as *mut _
4431}
4432
4433/// Returns a path from a root type down to a current shared collection (which can be obtained using
4434/// `yxmltext_event_path` function). It can consist of either integer indexes (used by sequence
4435/// components) or *char keys (used by map components). `len` output parameter is used to provide
4436/// information about length of the path.
4437///
4438/// Path returned this way should be eventually released using `ypath_destroy`.
4439#[no_mangle]
4440pub unsafe extern "C" fn yxmltext_event_path(
4441    e: *const YXmlTextEvent,
4442    len: *mut u32,
4443) -> *mut YPathSegment {
4444    assert!(!e.is_null());
4445    let e = &*e;
4446    let path: Vec<_> = e.path().into_iter().map(YPathSegment::from).collect();
4447    let out = path.into_boxed_slice();
4448    *len = out.len() as u32;
4449    Box::into_raw(out) as *mut _
4450}
4451
4452/// Returns a path from a root type down to a current shared collection (which can be obtained using
4453/// `yarray_event_target` function). It can consist of either integer indexes (used by sequence
4454/// components) or *char keys (used by map components). `len` output parameter is used to provide
4455/// information about length of the path.
4456///
4457/// Path returned this way should be eventually released using `ypath_destroy`.
4458#[no_mangle]
4459pub unsafe extern "C" fn yarray_event_path(
4460    e: *const YArrayEvent,
4461    len: *mut u32,
4462) -> *mut YPathSegment {
4463    assert!(!e.is_null());
4464    let e = &*e;
4465    let path: Vec<_> = e.path().into_iter().map(YPathSegment::from).collect();
4466    let out = path.into_boxed_slice();
4467    *len = out.len() as u32;
4468    Box::into_raw(out) as *mut _
4469}
4470
4471/// Releases allocated memory used by objects returned from path accessor functions of shared type
4472/// events.
4473#[no_mangle]
4474pub unsafe extern "C" fn ypath_destroy(path: *mut YPathSegment, len: u32) {
4475    if !path.is_null() {
4476        drop(Vec::from_raw_parts(path, len as usize, len as usize));
4477    }
4478}
4479
4480/// Returns a sequence of changes produced by sequence component of shared collections (such as
4481/// `YText`, `YXmlText` and XML nodes added to `YXmlElement`). `len` output parameter is used to
4482/// provide information about number of changes produced.
4483///
4484/// Delta returned from this function should eventually be released using `ytext_delta_destroy`
4485/// function.
4486#[no_mangle]
4487pub unsafe extern "C" fn ytext_event_delta(e: *const YTextEvent, len: *mut u32) -> *mut YDeltaOut {
4488    assert!(!e.is_null());
4489    let e = &*e;
4490    let delta: Vec<_> = e.delta(e.txn()).into_iter().map(YDeltaOut::from).collect();
4491
4492    let out = delta.into_boxed_slice();
4493    *len = out.len() as u32;
4494    Box::into_raw(out) as *mut _
4495}
4496
4497/// Returns a sequence of changes produced by sequence component of shared collections (such as
4498/// `YText`, `YXmlText` and XML nodes added to `YXmlElement`). `len` output parameter is used to
4499/// provide information about number of changes produced.
4500///
4501/// Delta returned from this function should eventually be released using `ytext_delta_destroy`
4502/// function.
4503#[no_mangle]
4504pub unsafe extern "C" fn yxmltext_event_delta(
4505    e: *const YXmlTextEvent,
4506    len: *mut u32,
4507) -> *mut YDeltaOut {
4508    assert!(!e.is_null());
4509    let e = &*e;
4510    let delta: Vec<_> = e.delta(e.txn()).into_iter().map(YDeltaOut::from).collect();
4511
4512    let out = delta.into_boxed_slice();
4513    *len = out.len() as u32;
4514    Box::into_raw(out) as *mut _
4515}
4516
4517/// Returns a sequence of changes produced by sequence component of shared collections (such as
4518/// `YText`, `YXmlText` and XML nodes added to `YXmlElement`). `len` output parameter is used to
4519/// provide information about number of changes produced.
4520///
4521/// Delta returned from this function should eventually be released using `yevent_delta_destroy`
4522/// function.
4523#[no_mangle]
4524pub unsafe extern "C" fn yarray_event_delta(
4525    e: *const YArrayEvent,
4526    len: *mut u32,
4527) -> *mut YEventChange {
4528    assert!(!e.is_null());
4529    let e = &*e;
4530    let delta: Vec<_> = e
4531        .delta(e.txn())
4532        .into_iter()
4533        .map(YEventChange::from)
4534        .collect();
4535
4536    let out = delta.into_boxed_slice();
4537    *len = out.len() as u32;
4538    Box::into_raw(out) as *mut _
4539}
4540
4541/// Returns a sequence of changes produced by sequence component of shared collections (such as
4542/// `YText`, `YXmlText` and XML nodes added to `YXmlElement`). `len` output parameter is used to
4543/// provide information about number of changes produced.
4544///
4545/// Delta returned from this function should eventually be released using `yevent_delta_destroy`
4546/// function.
4547#[no_mangle]
4548pub unsafe extern "C" fn yxmlelem_event_delta(
4549    e: *const YXmlEvent,
4550    len: *mut u32,
4551) -> *mut YEventChange {
4552    assert!(!e.is_null());
4553    let e = &*e;
4554    let delta: Vec<_> = e
4555        .delta(e.txn())
4556        .into_iter()
4557        .map(YEventChange::from)
4558        .collect();
4559
4560    let out = delta.into_boxed_slice();
4561    *len = out.len() as u32;
4562    Box::into_raw(out) as *mut _
4563}
4564
4565/// Releases memory allocated by the object returned from `ytext_delta` function.
4566#[no_mangle]
4567pub unsafe extern "C" fn ytext_delta_destroy(delta: *mut YDeltaOut, len: u32) {
4568    if !delta.is_null() {
4569        let delta = Vec::from_raw_parts(delta, len as usize, len as usize);
4570        drop(delta);
4571    }
4572}
4573
4574/// Releases memory allocated by the object returned from `yevent_delta` function.
4575#[no_mangle]
4576pub unsafe extern "C" fn yevent_delta_destroy(delta: *mut YEventChange, len: u32) {
4577    if !delta.is_null() {
4578        let delta = Vec::from_raw_parts(delta, len as usize, len as usize);
4579        drop(delta);
4580    }
4581}
4582
4583/// Returns a sequence of changes produced by map component of shared collections (such as
4584/// `YMap` and `YXmlText`/`YXmlElement` attribute changes). `len` output parameter is used to
4585/// provide information about number of changes produced.
4586///
4587/// Delta returned from this function should eventually be released using `yevent_keys_destroy`
4588/// function.
4589#[no_mangle]
4590pub unsafe extern "C" fn ymap_event_keys(
4591    e: *const YMapEvent,
4592    len: *mut u32,
4593) -> *mut YEventKeyChange {
4594    assert!(!e.is_null());
4595    let e = &*e;
4596    let delta: Vec<_> = e
4597        .keys(e.txn())
4598        .into_iter()
4599        .map(|(k, v)| YEventKeyChange::new(k.as_ref(), v))
4600        .collect();
4601
4602    let out = delta.into_boxed_slice();
4603    *len = out.len() as u32;
4604    Box::into_raw(out) as *mut _
4605}
4606
4607/// Returns a sequence of changes produced by map component of shared collections.
4608/// `len` output parameter is used to provide information about number of changes produced.
4609///
4610/// Delta returned from this function should eventually be released using `yevent_keys_destroy`
4611/// function.
4612#[no_mangle]
4613pub unsafe extern "C" fn yxmlelem_event_keys(
4614    e: *const YXmlEvent,
4615    len: *mut u32,
4616) -> *mut YEventKeyChange {
4617    assert!(!e.is_null());
4618    let e = &*e;
4619    let delta: Vec<_> = e
4620        .keys(e.txn())
4621        .into_iter()
4622        .map(|(k, v)| YEventKeyChange::new(k.as_ref(), v))
4623        .collect();
4624
4625    let out = delta.into_boxed_slice();
4626    *len = out.len() as u32;
4627    Box::into_raw(out) as *mut _
4628}
4629
4630/// Returns a sequence of changes produced by map component of shared collections.
4631/// `len` output parameter is used to provide information about number of changes produced.
4632///
4633/// Delta returned from this function should eventually be released using `yevent_keys_destroy`
4634/// function.
4635#[no_mangle]
4636pub unsafe extern "C" fn yxmltext_event_keys(
4637    e: *const YXmlTextEvent,
4638    len: *mut u32,
4639) -> *mut YEventKeyChange {
4640    assert!(!e.is_null());
4641    let e = &*e;
4642    let delta: Vec<_> = e
4643        .keys(e.txn())
4644        .into_iter()
4645        .map(|(k, v)| YEventKeyChange::new(k.as_ref(), v))
4646        .collect();
4647
4648    let out = delta.into_boxed_slice();
4649    *len = out.len() as u32;
4650    Box::into_raw(out) as *mut _
4651}
4652
4653/// Releases memory allocated by the object returned from `yxml_event_keys` and `ymap_event_keys`
4654/// functions.
4655#[no_mangle]
4656pub unsafe extern "C" fn yevent_keys_destroy(keys: *mut YEventKeyChange, len: u32) {
4657    if !keys.is_null() {
4658        drop(Vec::from_raw_parts(keys, len as usize, len as usize));
4659    }
4660}
4661
4662pub type YUndoManager = yrs::undo::UndoManager<AtomicPtr<c_void>>;
4663
4664#[repr(C)]
4665pub struct YUndoManagerOptions {
4666    pub capture_timeout_millis: i32,
4667}
4668
4669// TODO [LSViana] Maybe rename this to `yundo_manager_new_with_options` to match `ydoc_new_with_options`?
4670/// Creates a new instance of undo manager bound to a current `doc`. It can be used to track
4671/// specific shared refs via `yundo_manager_add_scope` and updates coming from specific origin
4672/// - like ability to undo/redo operations originating only at the local peer - by using
4673/// `yundo_manager_add_origin`.
4674///
4675/// This object can be deallocated via `yundo_manager_destroy`.
4676#[no_mangle]
4677pub unsafe extern "C" fn yundo_manager(
4678    doc: *const Doc,
4679    options: *const YUndoManagerOptions,
4680) -> *mut YUndoManager {
4681    let doc = doc.as_ref().unwrap();
4682
4683    let mut o = yrs::undo::Options::default();
4684    if let Some(options) = options.as_ref() {
4685        if options.capture_timeout_millis >= 0 {
4686            o.capture_timeout_millis = options.capture_timeout_millis as u64;
4687        }
4688    };
4689    let boxed = Box::new(yrs::undo::UndoManager::with_options(doc, o));
4690    Box::into_raw(boxed)
4691}
4692
4693/// Deallocated undo manager instance created via `yundo_manager`.
4694#[no_mangle]
4695pub unsafe extern "C" fn yundo_manager_destroy(mgr: *mut YUndoManager) {
4696    drop(Box::from_raw(mgr));
4697}
4698
4699/// Adds an origin to be tracked by current undo manager. This way only changes made within context
4700/// of transactions created with specific origin will be subjects of undo/redo operations. This is
4701/// useful when you want to be able to revert changed done by specific user without reverting
4702/// changes made by other users that were applied in the meantime.
4703#[no_mangle]
4704pub unsafe extern "C" fn yundo_manager_add_origin(
4705    mgr: *mut YUndoManager,
4706    origin_len: u32,
4707    origin: *const c_char,
4708) {
4709    let mgr = mgr.as_mut().unwrap();
4710    let bytes = std::slice::from_raw_parts(origin as *const u8, origin_len as usize);
4711    mgr.include_origin(Origin::from(bytes));
4712}
4713
4714/// Removes an origin previously added to undo manager via `yundo_manager_add_origin`.
4715#[no_mangle]
4716pub unsafe extern "C" fn yundo_manager_remove_origin(
4717    mgr: *mut YUndoManager,
4718    origin_len: u32,
4719    origin: *const c_char,
4720) {
4721    let mgr = mgr.as_mut().unwrap();
4722    let bytes = std::slice::from_raw_parts(origin as *const u8, origin_len as usize);
4723    mgr.exclude_origin(Origin::from(bytes));
4724}
4725
4726/// Add specific shared type to be tracked by this instance of an undo manager.
4727#[no_mangle]
4728pub unsafe extern "C" fn yundo_manager_add_scope(mgr: *mut YUndoManager, ytype: *const Branch) {
4729    let mgr = mgr.as_mut().unwrap();
4730    let branch = ytype.as_ref().unwrap();
4731    mgr.expand_scope(&BranchPtr::from(branch));
4732}
4733
4734/// Removes all the undo/redo stack changes tracked by current undo manager. This also cleans up
4735/// all the items that couldn't be deallocated / garbage collected for the sake of possible
4736/// undo/redo operations.
4737///
4738/// Keep in mind that this function call requires that underlying document store is not concurrently
4739/// modified by other read-write transaction. This is done by acquiring the read-only transaction
4740/// itself. If such transaction could be acquired (because of another read-write transaction is in
4741/// progress, this function will hold current thread until acquisition is possible.
4742#[no_mangle]
4743pub unsafe extern "C" fn yundo_manager_clear(mgr: *mut YUndoManager) {
4744    let mgr = mgr.as_mut().unwrap();
4745    mgr.clear();
4746}
4747
4748/// Cuts off tracked changes, producing a new stack item on undo stack.
4749///
4750/// By default, undo manager gathers undergoing changes together into undo stack items on periodic
4751/// basis (defined by `YUndoManagerOptions.capture_timeout_millis`). By calling this function, we're
4752/// explicitly creating a new stack item will all the changes registered since last stack item was
4753/// created.
4754#[no_mangle]
4755pub unsafe extern "C" fn yundo_manager_stop(mgr: *mut YUndoManager) {
4756    let mgr = mgr.as_mut().unwrap();
4757    mgr.reset();
4758}
4759
4760/// Performs an undo operations, reverting all the changes defined by the last undo stack item.
4761/// These changes can be then reapplied again by calling `yundo_manager_redo` function.
4762///
4763/// Returns `Y_TRUE` if successfully managed to do an undo operation.
4764/// Returns `Y_FALSE` if undo stack was empty or if undo couldn't be performed (because another
4765/// transaction is in progress).
4766#[no_mangle]
4767pub unsafe extern "C" fn yundo_manager_undo(mgr: *mut YUndoManager) -> u8 {
4768    let mgr = mgr.as_mut().unwrap();
4769
4770    match mgr.try_undo() {
4771        Ok(true) => Y_TRUE,
4772        Ok(false) => Y_FALSE,
4773        Err(_) => Y_FALSE,
4774    }
4775}
4776
4777/// Performs a redo operations, reapplying changes undone by `yundo_manager_undo` operation.
4778///
4779/// Returns `Y_TRUE` if successfully managed to do a redo operation.
4780/// Returns `Y_FALSE` if redo stack was empty or if redo couldn't be performed (because another
4781/// transaction is in progress).
4782#[no_mangle]
4783pub unsafe extern "C" fn yundo_manager_redo(mgr: *mut YUndoManager) -> u8 {
4784    let mgr = mgr.as_mut().unwrap();
4785    match mgr.try_redo() {
4786        Ok(true) => Y_TRUE,
4787        Ok(false) => Y_FALSE,
4788        Err(_) => Y_FALSE,
4789    }
4790}
4791
4792/// Returns number of elements stored on undo stack.
4793#[no_mangle]
4794pub unsafe extern "C" fn yundo_manager_undo_stack_len(mgr: *mut YUndoManager) -> u32 {
4795    let mgr = mgr.as_mut().unwrap();
4796    mgr.undo_stack().len() as u32
4797}
4798
4799/// Returns number of elements stored on redo stack.
4800#[no_mangle]
4801pub unsafe extern "C" fn yundo_manager_redo_stack_len(mgr: *mut YUndoManager) -> u32 {
4802    let mgr = mgr.as_mut().unwrap();
4803    mgr.redo_stack().len() as u32
4804}
4805
4806/// Subscribes a `callback` function pointer to a given undo manager event. This event will be
4807/// triggered every time a new undo/redo stack item is added.
4808///
4809/// Returns a subscription pointer that can be used to cancel current callback registration via
4810/// `yunobserve`.
4811#[no_mangle]
4812pub unsafe extern "C" fn yundo_manager_observe_added(
4813    mgr: *mut YUndoManager,
4814    state: *mut c_void,
4815    callback: extern "C" fn(*mut c_void, *const YUndoEvent),
4816) -> *mut Subscription {
4817    let state = CallbackState::new(state);
4818    let mgr = mgr.as_mut().unwrap();
4819    let subscription = mgr.observe_item_added(move |_, e| {
4820        let meta_ptr = {
4821            let event = YUndoEvent::new(e);
4822            callback(state.0, &event as *const YUndoEvent);
4823            event.meta
4824        };
4825        e.meta().store(meta_ptr, Ordering::Release);
4826    });
4827    Box::into_raw(Box::new(subscription))
4828}
4829
4830/// Subscribes a `callback` function pointer to a given undo manager event. This event will be
4831/// triggered every time a undo/redo operation was called.
4832///
4833/// Returns a subscription pointer that can be used to cancel current callback registration via
4834/// `yunobserve`.
4835#[no_mangle]
4836pub unsafe extern "C" fn yundo_manager_observe_popped(
4837    mgr: *mut YUndoManager,
4838    state: *mut c_void,
4839    callback: extern "C" fn(*mut c_void, *const YUndoEvent),
4840) -> *mut Subscription {
4841    let mgr = mgr.as_mut().unwrap();
4842    let state = CallbackState::new(state);
4843    let subscription = mgr
4844        .observe_item_popped(move |_, e| {
4845            let meta_ptr = {
4846                let event = YUndoEvent::new(e);
4847                callback(state.0, &event as *const YUndoEvent);
4848                event.meta
4849            };
4850            e.meta().store(meta_ptr, Ordering::Release);
4851        })
4852        .into();
4853    Box::into_raw(Box::new(subscription))
4854}
4855
4856pub const Y_KIND_UNDO: c_char = 0;
4857pub const Y_KIND_REDO: c_char = 1;
4858
4859/// Event type related to `UndoManager` observer operations, such as `yundo_manager_observe_popped`
4860/// and `yundo_manager_observe_added`. It contains various informations about the context in which
4861/// undo/redo operations are executed.
4862#[repr(C)]
4863pub struct YUndoEvent {
4864    /// Informs if current event is related to executed undo (`Y_KIND_UNDO`) or redo (`Y_KIND_REDO`)
4865    /// operation.
4866    pub kind: c_char,
4867    /// Origin assigned to a transaction, in context of which this event is being executed.
4868    /// Transaction origin is specified via `ydoc_write_transaction(doc, origin_len, origin)`.
4869    pub origin: *const c_char,
4870    /// Length of an `origin` field assigned to a transaction, in context of which this event is
4871    /// being executed.
4872    /// Transaction origin is specified via `ydoc_write_transaction(doc, origin_len, origin)`.
4873    pub origin_len: u32,
4874    /// Pointer to a custom metadata object that can be passed between
4875    /// `yundo_manager_observe_popped` and `yundo_manager_observe_added`. It's useful for passing
4876    /// around custom user data ie. cursor position, that needs to be remembered and restored as
4877    /// part of undo/redo operations.
4878    ///
4879    /// This field always starts with no value (`NULL`) assigned to it and can be set/unset in
4880    /// corresponding callback calls. In such cases it's up to a programmer to handle allocation
4881    /// and deallocation of memory that this pointer will point to. Not releasing it properly may
4882    /// lead to memory leaks.
4883    pub meta: *mut c_void,
4884}
4885
4886impl YUndoEvent {
4887    unsafe fn new(e: &yrs::undo::Event<AtomicPtr<c_void>>) -> Self {
4888        let (origin, origin_len) = if let Some(origin) = e.origin() {
4889            let bytes = origin.as_ref();
4890            let origin_len = bytes.len() as u32;
4891            let origin = bytes.as_ptr() as *const c_char;
4892            (origin, origin_len)
4893        } else {
4894            (null(), 0)
4895        };
4896        YUndoEvent {
4897            kind: match e.kind() {
4898                EventKind::Undo => Y_KIND_UNDO,
4899                EventKind::Redo => Y_KIND_REDO,
4900            },
4901            origin,
4902            origin_len,
4903            meta: e.meta().load(Ordering::Acquire),
4904        }
4905    }
4906}
4907
4908/// Returns a value informing what kind of Yrs shared collection given `branch` represents.
4909/// Returns either 0 when `branch` is null or one of values: `Y_ARRAY`, `Y_TEXT`, `Y_MAP`,
4910/// `Y_XML_ELEM`, `Y_XML_TEXT`.
4911#[no_mangle]
4912pub unsafe extern "C" fn ytype_kind(branch: *const Branch) -> i8 {
4913    if let Some(branch) = branch.as_ref() {
4914        match branch.type_ref() {
4915            TypeRef::Array => Y_ARRAY,
4916            TypeRef::Map => Y_MAP,
4917            TypeRef::Text => Y_TEXT,
4918            TypeRef::XmlElement(_) => Y_XML_ELEM,
4919            TypeRef::XmlText => Y_XML_TEXT,
4920            TypeRef::XmlFragment => Y_XML_FRAG,
4921            TypeRef::SubDoc => Y_DOC,
4922            TypeRef::WeakLink(_) => Y_WEAK_LINK,
4923            TypeRef::XmlHook => 0,
4924            TypeRef::Undefined => 0,
4925        }
4926    } else {
4927        0
4928    }
4929}
4930
4931/// Tag used to identify `YPathSegment` storing a *char parameter.
4932pub const Y_EVENT_PATH_KEY: c_char = 1;
4933
4934/// Tag used to identify `YPathSegment` storing an int parameter.
4935pub const Y_EVENT_PATH_INDEX: c_char = 2;
4936
4937/// A single segment of a path returned from `yevent_path` function. It can be one of two cases,
4938/// recognized by it's `tag` field:
4939///
4940/// 1. `Y_EVENT_PATH_KEY` means that segment value can be accessed by `segment.value.key` and is
4941/// referring to a string key used by map component (eg. `YMap` entry).
4942/// 2. `Y_EVENT_PATH_INDEX` means that segment value can be accessed by `segment.value.index` and is
4943/// referring to an int index used by sequence component (eg. `YArray` item or `YXmlElement` child).
4944#[repr(C)]
4945pub struct YPathSegment {
4946    /// Tag used to identify which case current segment is referring to:
4947    ///
4948    /// 1. `Y_EVENT_PATH_KEY` means that segment value can be accessed by `segment.value.key` and is
4949    /// referring to a string key used by map component (eg. `YMap` entry).
4950    /// 2. `Y_EVENT_PATH_INDEX` means that segment value can be accessed by `segment.value.index`
4951    /// and is referring to an int index used by sequence component (eg. `YArray` item or
4952    /// `YXmlElement` child).
4953    pub tag: c_char,
4954
4955    /// Union field containing either `key` or `index`. A particular case can be recognized by using
4956    /// segment's `tag` field.
4957    pub value: YPathSegmentCase,
4958}
4959
4960impl From<PathSegment> for YPathSegment {
4961    fn from(ps: PathSegment) -> Self {
4962        match ps {
4963            PathSegment::Key(key) => {
4964                let key = CString::new(key.as_ref()).unwrap().into_raw() as *const _;
4965                YPathSegment {
4966                    tag: Y_EVENT_PATH_KEY,
4967                    value: YPathSegmentCase { key },
4968                }
4969            }
4970            PathSegment::Index(index) => YPathSegment {
4971                tag: Y_EVENT_PATH_INDEX,
4972                value: YPathSegmentCase {
4973                    index: index as u32,
4974                },
4975            },
4976        }
4977    }
4978}
4979
4980impl Drop for YPathSegment {
4981    fn drop(&mut self) {
4982        if self.tag == Y_EVENT_PATH_KEY {
4983            unsafe {
4984                ystring_destroy(self.value.key as *mut _);
4985            }
4986        }
4987    }
4988}
4989
4990#[repr(C)]
4991pub union YPathSegmentCase {
4992    pub key: *const c_char,
4993    pub index: u32,
4994}
4995
4996/// Tag used to identify `YEventChange` (see: `yevent_delta` function) case, when a new element
4997/// has been added to an observed collection.
4998pub const Y_EVENT_CHANGE_ADD: u8 = 1;
4999
5000/// Tag used to identify `YEventChange` (see: `yevent_delta` function) case, when an existing
5001/// element has been removed from an observed collection.
5002pub const Y_EVENT_CHANGE_DELETE: u8 = 2;
5003
5004/// Tag used to identify `YEventChange` (see: `yevent_delta` function) case, when no changes have
5005/// been detected for a particular range of observed collection.
5006pub const Y_EVENT_CHANGE_RETAIN: u8 = 3;
5007
5008/// A data type representing a single change detected over an observed shared collection. A type
5009/// of change can be detected using a `tag` field:
5010///
5011/// 1. `Y_EVENT_CHANGE_ADD` marks a new elements added to a collection. In this case `values` field
5012/// contains a pointer to a list of newly inserted values, while `len` field informs about their
5013/// count.
5014/// 2. `Y_EVENT_CHANGE_DELETE` marks an existing elements removed from the collection. In this case
5015/// `len` field informs about number of removed elements.
5016/// 3. `Y_EVENT_CHANGE_RETAIN` marks a number of elements that have not been changed, counted from
5017/// the previous element. `len` field informs about number of retained elements.
5018///
5019/// A list of changes returned by `yarray_event_delta`/`yxml_event_delta` enables to locate a
5020/// position of all changes within an observed collection by using a combination of added/deleted
5021/// change structs separated by retained changes (marking eg. number of elements that can be safely
5022/// skipped, since they remained unchanged).
5023#[repr(C)]
5024pub struct YEventChange {
5025    /// Tag field used to identify particular type of change made:
5026    ///
5027    /// 1. `Y_EVENT_CHANGE_ADD` marks a new elements added to a collection. In this case `values`
5028    /// field contains a pointer to a list of newly inserted values, while `len` field informs about
5029    /// their count.
5030    /// 2. `Y_EVENT_CHANGE_DELETE` marks an existing elements removed from the collection. In this
5031    /// case `len` field informs about number of removed elements.
5032    /// 3. `Y_EVENT_CHANGE_RETAIN` marks a number of elements that have not been changed, counted
5033    /// from the previous element. `len` field informs about number of retained elements.
5034    pub tag: u8,
5035
5036    /// Number of element affected by current type of a change. It can refer to a number of
5037    /// inserted `values`, number of deleted element or a number of retained (unchanged) values.
5038    pub len: u32,
5039
5040    /// Used in case when current change is of `Y_EVENT_CHANGE_ADD` type. Contains a list (of
5041    /// length stored in `len` field) of newly inserted values.
5042    pub values: *const YOutput,
5043}
5044
5045impl<'a> From<&'a Change> for YEventChange {
5046    fn from(change: &'a Change) -> Self {
5047        match change {
5048            Change::Added(values) => {
5049                let out: Vec<_> = values
5050                    .into_iter()
5051                    .map(|v| YOutput::from(v.clone()))
5052                    .collect();
5053                let len = out.len() as u32;
5054                let out = out.into_boxed_slice();
5055                let values = Box::into_raw(out) as *mut _;
5056
5057                YEventChange {
5058                    tag: Y_EVENT_CHANGE_ADD,
5059                    len,
5060                    values,
5061                }
5062            }
5063            Change::Removed(len) => YEventChange {
5064                tag: Y_EVENT_CHANGE_DELETE,
5065                len: *len as u32,
5066                values: null(),
5067            },
5068            Change::Retain(len) => YEventChange {
5069                tag: Y_EVENT_CHANGE_RETAIN,
5070                len: *len as u32,
5071                values: null(),
5072            },
5073        }
5074    }
5075}
5076
5077impl Drop for YEventChange {
5078    fn drop(&mut self) {
5079        if self.tag == Y_EVENT_CHANGE_ADD {
5080            unsafe {
5081                let len = self.len as usize;
5082                let values = Vec::from_raw_parts(self.values as *mut YOutput, len, len);
5083                drop(values);
5084            }
5085        }
5086    }
5087}
5088
5089/// A data type representing a single change detected over an observed `YText`/`YXmlText`. A type
5090/// of change can be detected using a `tag` field:
5091///
5092/// 1. `Y_EVENT_CHANGE_ADD` marks a new characters added to a collection. In this case `insert`
5093/// field contains a pointer to a list of newly inserted values, while `len` field informs about
5094/// their count. Additionally `attributes_len` and `attributes` carry information about optional
5095/// formatting attributes applied to edited blocks.
5096/// 2. `Y_EVENT_CHANGE_DELETE` marks an existing elements removed from the collection. In this case
5097/// `len` field informs about number of removed elements.
5098/// 3. `Y_EVENT_CHANGE_RETAIN` marks a number of characters that have not been changed, counted from
5099/// the previous element. `len` field informs about number of retained elements. Additionally
5100/// `attributes_len` and `attributes` carry information about optional formatting attributes applied
5101/// to edited blocks.
5102///
5103/// A list of changes returned by `ytext_event_delta`/`yxmltext_event_delta` enables to locate
5104/// a position of all changes within an observed collection by using a combination of added/deleted
5105/// change structs separated by retained changes (marking eg. number of elements that can be safely
5106/// skipped, since they remained unchanged).
5107#[repr(C)]
5108pub struct YDeltaOut {
5109    /// Tag field used to identify particular type of change made:
5110    ///
5111    /// 1. `Y_EVENT_CHANGE_ADD` marks a new elements added to a collection. In this case `values`
5112    /// field contains a pointer to a list of newly inserted values, while `len` field informs about
5113    /// their count.
5114    /// 2. `Y_EVENT_CHANGE_DELETE` marks an existing elements removed from the collection. In this
5115    /// case `len` field informs about number of removed elements.
5116    /// 3. `Y_EVENT_CHANGE_RETAIN` marks a number of elements that have not been changed, counted
5117    /// from the previous element. `len` field informs about number of retained elements.
5118    pub tag: u8,
5119
5120    /// Number of element affected by current type of change. It can refer to a number of
5121    /// inserted `values`, number of deleted element or a number of retained (unchanged) values.
5122    pub len: u32,
5123
5124    /// A number of formatting attributes assigned to an edited area represented by this delta.
5125    pub attributes_len: u32,
5126
5127    /// A nullable pointer to a list of formatting attributes assigned to an edited area represented
5128    /// by this delta.
5129    pub attributes: *mut YDeltaAttr,
5130
5131    /// Used in case when current change is of `Y_EVENT_CHANGE_ADD` type. Contains a list (of
5132    /// length stored in `len` field) of newly inserted values.
5133    pub insert: *mut YOutput,
5134}
5135
5136impl YDeltaOut {
5137    fn insert(value: &Out, attrs: &Option<Box<Attrs>>) -> Self {
5138        let insert = Box::into_raw(Box::new(YOutput::from(value.clone())));
5139        let (attributes_len, attributes) = if let Some(attrs) = attrs {
5140            let len = attrs.len() as u32;
5141            let attrs: Vec<_> = attrs.iter().map(|(k, v)| YDeltaAttr::new(k, v)).collect();
5142            let attrs = Box::into_raw(attrs.into_boxed_slice()) as *mut _;
5143            (len, attrs)
5144        } else {
5145            (0, null_mut())
5146        };
5147
5148        YDeltaOut {
5149            tag: Y_EVENT_CHANGE_ADD,
5150            len: 1,
5151            insert,
5152            attributes_len,
5153            attributes,
5154        }
5155    }
5156
5157    fn retain(len: u32, attrs: &Option<Box<Attrs>>) -> Self {
5158        let (attributes_len, attributes) = if let Some(attrs) = attrs {
5159            let len = attrs.len() as u32;
5160            let attrs: Vec<_> = attrs.iter().map(|(k, v)| YDeltaAttr::new(k, v)).collect();
5161            let attrs = Box::into_raw(attrs.into_boxed_slice()) as *mut _;
5162            (len, attrs)
5163        } else {
5164            (0, null_mut())
5165        };
5166        YDeltaOut {
5167            tag: Y_EVENT_CHANGE_RETAIN,
5168            len,
5169            insert: null_mut(),
5170            attributes_len,
5171            attributes,
5172        }
5173    }
5174
5175    fn delete(len: u32) -> Self {
5176        YDeltaOut {
5177            tag: Y_EVENT_CHANGE_DELETE,
5178            len,
5179            insert: null_mut(),
5180            attributes_len: 0,
5181            attributes: null_mut(),
5182        }
5183    }
5184}
5185
5186impl<'a> From<&'a Delta> for YDeltaOut {
5187    fn from(d: &Delta) -> Self {
5188        match d {
5189            Delta::Inserted(value, attrs) => YDeltaOut::insert(value, attrs),
5190            Delta::Retain(len, attrs) => YDeltaOut::retain(*len, attrs),
5191            Delta::Deleted(len) => YDeltaOut::delete(*len),
5192        }
5193    }
5194}
5195
5196impl Drop for YDeltaOut {
5197    fn drop(&mut self) {
5198        unsafe {
5199            if !self.attributes.is_null() {
5200                let len = self.attributes_len as usize;
5201                drop(Vec::from_raw_parts(self.attributes, len, len));
5202            }
5203            if !self.insert.is_null() {
5204                drop(Box::from_raw(self.insert));
5205            }
5206        }
5207    }
5208}
5209
5210/// A single instance of formatting attribute stored as part of `YDelta` instance.
5211#[repr(C)]
5212pub struct YDeltaAttr {
5213    /// A null-terminated UTF-8 encoded string containing a unique formatting attribute name.
5214    pub key: *const c_char,
5215    /// A value assigned to a formatting attribute.
5216    pub value: YOutput,
5217}
5218
5219impl YDeltaAttr {
5220    fn new(k: &Arc<str>, v: &Any) -> Self {
5221        let key = CString::new(k.as_ref()).unwrap().into_raw() as *const _;
5222        let value = YOutput::from(v);
5223        YDeltaAttr { key, value }
5224    }
5225}
5226
5227impl Drop for YDeltaAttr {
5228    fn drop(&mut self) {
5229        unsafe { ystring_destroy(self.key as *mut _) }
5230    }
5231}
5232
5233/// A data type representing a single change to be performed in sequence of changes defined
5234/// as parameter to a `ytext_insert_delta` function. A type of change can be detected using
5235/// a `tag` field:
5236///
5237/// 1. `Y_EVENT_CHANGE_ADD` marks a new characters added to a collection. In this case `insert`
5238/// field contains a pointer to a list of newly inserted values, while `len` field informs about
5239/// their count. Additionally `attributes_len` and `attributes` carry information about optional
5240/// formatting attributes applied to edited blocks.
5241/// 2. `Y_EVENT_CHANGE_DELETE` marks an existing elements removed from the collection. In this case
5242/// `len` field informs about number of removed elements.
5243/// 3. `Y_EVENT_CHANGE_RETAIN` marks a number of characters that have not been changed, counted from
5244/// the previous element. `len` field informs about number of retained elements. Additionally
5245/// `attributes_len` and `attributes` carry information about optional formatting attributes applied
5246/// to edited blocks.
5247#[repr(C)]
5248pub struct YDeltaIn {
5249    /// Tag field used to identify particular type of change made:
5250    ///
5251    /// 1. `Y_EVENT_CHANGE_ADD` marks a new elements added to a collection. In this case `values`
5252    /// field contains a pointer to a list of newly inserted values, while `len` field informs about
5253    /// their count.
5254    /// 2. `Y_EVENT_CHANGE_DELETE` marks an existing elements removed from the collection. In this
5255    /// case `len` field informs about number of removed elements.
5256    /// 3. `Y_EVENT_CHANGE_RETAIN` marks a number of elements that have not been changed, counted
5257    /// from the previous element. `len` field informs about number of retained elements.
5258    pub tag: u8,
5259
5260    /// Number of element affected by current type of change. It can refer to a number of
5261    /// inserted `values`, number of deleted element or a number of retained (unchanged) values.
5262    pub len: u32,
5263
5264    /// A nullable pointer to a list of formatting attributes assigned to an edited area represented
5265    /// by this delta.
5266    pub attributes: *const YInput,
5267
5268    /// Used in case when current change is of `Y_EVENT_CHANGE_ADD` type. Contains a list (of
5269    /// length stored in `len` field) of newly inserted values.
5270    pub insert: *const YInput,
5271}
5272
5273impl YDeltaIn {
5274    fn as_input(&self) -> Delta<YInput> {
5275        match self.tag {
5276            Y_EVENT_CHANGE_RETAIN => {
5277                let attrs = if self.attributes.is_null() {
5278                    None
5279                } else {
5280                    let attrs = unsafe { self.attributes.read() };
5281                    map_attrs(attrs.into()).map(Box::new)
5282                };
5283                Delta::Retain(self.len, attrs)
5284            }
5285            Y_EVENT_CHANGE_DELETE => Delta::Deleted(self.len),
5286            Y_EVENT_CHANGE_ADD => {
5287                let attrs = if self.attributes.is_null() {
5288                    None
5289                } else {
5290                    let attrs = unsafe { self.attributes.read() };
5291                    map_attrs(attrs.into()).map(Box::new)
5292                };
5293                let input = unsafe { self.insert.read() };
5294                Delta::Inserted(input, attrs)
5295            }
5296            tag => panic!("YDelta tag identifier is of unknown type: {}", tag),
5297        }
5298    }
5299}
5300
5301/// Tag used to identify `YEventKeyChange` (see: `yevent_keys` function) case, when a new entry has
5302/// been inserted into a map component of shared collection.
5303pub const Y_EVENT_KEY_CHANGE_ADD: c_char = 4;
5304
5305/// Tag used to identify `YEventKeyChange` (see: `yevent_keys` function) case, when an existing
5306/// entry has been removed from a map component of shared collection.
5307pub const Y_EVENT_KEY_CHANGE_DELETE: c_char = 5;
5308
5309/// Tag used to identify `YEventKeyChange` (see: `yevent_keys` function) case, when an existing
5310/// entry has been overridden with a new value within a map component of shared collection.
5311pub const Y_EVENT_KEY_CHANGE_UPDATE: c_char = 6;
5312
5313/// A data type representing a single change made over a map component of shared collection types,
5314/// such as `YMap` entries or `YXmlText`/`YXmlElement` attributes. A `key` field provides a
5315/// corresponding unique key string of a changed entry, while `tag` field informs about specific
5316/// type of change being done:
5317///
5318/// 1. `Y_EVENT_KEY_CHANGE_ADD` used to identify a newly added entry. In this case an `old_value`
5319/// field is NULL, while `new_value` field contains an inserted value.
5320/// 1. `Y_EVENT_KEY_CHANGE_DELETE` used to identify an existing entry being removed. In this case
5321/// an `old_value` field contains the removed value.
5322/// 1. `Y_EVENT_KEY_CHANGE_UPDATE` used to identify an existing entry, which value has been changed.
5323/// In this case `old_value` field contains replaced value, while `new_value` contains a newly
5324/// inserted one.
5325#[repr(C)]
5326pub struct YEventKeyChange {
5327    /// A UTF8-encoded null-terminated string containing a key of a changed entry.
5328    pub key: *const c_char,
5329    /// Tag field informing about type of change current struct refers to:
5330    ///
5331    /// 1. `Y_EVENT_KEY_CHANGE_ADD` used to identify a newly added entry. In this case an
5332    /// `old_value` field is NULL, while `new_value` field contains an inserted value.
5333    /// 1. `Y_EVENT_KEY_CHANGE_DELETE` used to identify an existing entry being removed. In this
5334    /// case an `old_value` field contains the removed value.
5335    /// 1. `Y_EVENT_KEY_CHANGE_UPDATE` used to identify an existing entry, which value has been
5336    /// changed. In this case `old_value` field contains replaced value, while `new_value` contains
5337    /// a newly inserted one.
5338    pub tag: c_char,
5339
5340    /// Contains a removed entry's value or replaced value of an updated entry.
5341    pub old_value: *const YOutput,
5342
5343    /// Contains a value of newly inserted entry or an updated entry's new value.
5344    pub new_value: *const YOutput,
5345}
5346
5347impl YEventKeyChange {
5348    fn new(key: &str, change: &EntryChange) -> Self {
5349        let key = CString::new(key).unwrap().into_raw() as *const _;
5350        match change {
5351            EntryChange::Inserted(new) => YEventKeyChange {
5352                key,
5353                tag: Y_EVENT_KEY_CHANGE_ADD,
5354                old_value: null(),
5355                new_value: Box::into_raw(Box::new(YOutput::from(new.clone()))),
5356            },
5357            EntryChange::Updated(old, new) => YEventKeyChange {
5358                key,
5359                tag: Y_EVENT_KEY_CHANGE_UPDATE,
5360                old_value: Box::into_raw(Box::new(YOutput::from(old.clone()))),
5361                new_value: Box::into_raw(Box::new(YOutput::from(new.clone()))),
5362            },
5363            EntryChange::Removed(old) => YEventKeyChange {
5364                key,
5365                tag: Y_EVENT_KEY_CHANGE_DELETE,
5366                old_value: Box::into_raw(Box::new(YOutput::from(old.clone()))),
5367                new_value: null(),
5368            },
5369        }
5370    }
5371}
5372
5373impl Drop for YEventKeyChange {
5374    fn drop(&mut self) {
5375        unsafe {
5376            ystring_destroy(self.key as *mut _);
5377            youtput_destroy(self.old_value as *mut _);
5378            youtput_destroy(self.new_value as *mut _);
5379        }
5380    }
5381}
5382
5383trait BranchPointable {
5384    fn into_raw_branch(self) -> *mut Branch;
5385    fn from_raw_branch(branch: *const Branch) -> Self;
5386}
5387
5388impl<T> BranchPointable for T
5389where
5390    T: AsRef<Branch> + From<BranchPtr>,
5391{
5392    fn into_raw_branch(self) -> *mut Branch {
5393        let branch_ref = self.as_ref();
5394        branch_ref as *const Branch as *mut Branch
5395    }
5396
5397    fn from_raw_branch(branch: *const Branch) -> Self {
5398        let b = unsafe { branch.as_ref().unwrap() };
5399        let branch_ref = BranchPtr::from(b);
5400        T::from(branch_ref)
5401    }
5402}
5403
5404/// A sticky index is based on the Yjs model and is not affected by document changes.
5405/// E.g. If you place a sticky index before a certain character, it will always point to this character.
5406/// If you place a sticky index at the end of a type, it will always point to the end of the type.
5407///
5408/// A numeric position is often unsuited for user selections, because it does not change when content is inserted
5409/// before or after.
5410///
5411/// ```Insert(0, 'x')('a.bc') = 'xa.bc'``` Where `.` is the sticky index position.
5412///
5413/// Instances of `YStickyIndex` can be freed using `ysticky_index_destroy`.
5414#[repr(transparent)]
5415pub struct YStickyIndex(StickyIndex);
5416
5417impl From<StickyIndex> for YStickyIndex {
5418    #[inline(always)]
5419    fn from(value: StickyIndex) -> Self {
5420        YStickyIndex(value)
5421    }
5422}
5423
5424/// Releases resources allocated by `YStickyIndex` pointers.
5425#[no_mangle]
5426pub unsafe extern "C" fn ysticky_index_destroy(pos: *mut YStickyIndex) {
5427    drop(Box::from_raw(pos))
5428}
5429
5430/// Returns association of current `YStickyIndex`.
5431/// If association is **after** the referenced inserted character, returned number will be >= 0.
5432/// If association is **before** the referenced inserted character, returned number will be < 0.
5433#[no_mangle]
5434pub unsafe extern "C" fn ysticky_index_assoc(pos: *const YStickyIndex) -> i8 {
5435    let pos = pos.as_ref().unwrap();
5436    match pos.0.assoc {
5437        Assoc::After => 0,
5438        Assoc::Before => -1,
5439    }
5440}
5441
5442/// Retrieves a `YStickyIndex` corresponding to a given human-readable `index` pointing into
5443/// the shared y-type `branch`. Unlike standard indexes sticky one enables to track
5444/// the location inside of a shared y-types, even in the face of concurrent updates.
5445///
5446/// If association is >= 0, the resulting position will point to location **after** the referenced index.
5447/// If association is < 0, the resulting position will point to location **before** the referenced index.
5448#[no_mangle]
5449pub unsafe extern "C" fn ysticky_index_from_index(
5450    branch: *const Branch,
5451    txn: *mut Transaction,
5452    index: u32,
5453    assoc: i8,
5454) -> *mut YStickyIndex {
5455    assert!(!branch.is_null());
5456    assert!(!txn.is_null());
5457
5458    let branch = BranchPtr::from_raw_branch(branch);
5459    let txn = txn.as_mut().unwrap();
5460    let index = index as u32;
5461    let assoc = if assoc >= 0 {
5462        Assoc::After
5463    } else {
5464        Assoc::Before
5465    };
5466
5467    if let Some(txn) = txn.as_mut() {
5468        if let Some(pos) = StickyIndex::at(txn, branch, index, assoc) {
5469            Box::into_raw(Box::new(YStickyIndex(pos)))
5470        } else {
5471            null_mut()
5472        }
5473    } else {
5474        panic!("ysticky_index_from_index requires a read-write transaction");
5475    }
5476}
5477
5478/// Serializes `YStickyIndex` into binary representation. `len` parameter is updated with byte
5479/// length of the generated binary. Returned binary can be free'd using `ybinary_destroy`.
5480#[no_mangle]
5481pub unsafe extern "C" fn ysticky_index_encode(
5482    pos: *const YStickyIndex,
5483    len: *mut u32,
5484) -> *mut c_char {
5485    let pos = pos.as_ref().unwrap();
5486    let binary = pos.0.encode_v1().into_boxed_slice();
5487    *len = binary.len() as u32;
5488    Box::into_raw(binary) as *mut c_char
5489}
5490
5491/// Serializes `YStickyIndex` into JSON representation. `len` parameter is updated with byte
5492/// length of the generated binary. Returned binary can be free'd using `ybinary_destroy`.
5493#[no_mangle]
5494pub unsafe extern "C" fn ysticky_index_decode(
5495    binary: *const c_char,
5496    len: u32,
5497) -> *mut YStickyIndex {
5498    let slice = std::slice::from_raw_parts(binary as *const u8, len as usize);
5499    if let Ok(pos) = StickyIndex::decode_v1(slice) {
5500        Box::into_raw(Box::new(YStickyIndex(pos)))
5501    } else {
5502        null_mut()
5503    }
5504}
5505
5506/// Serialize `YStickyIndex` into null-terminated UTF-8 encoded JSON string, that's compatible with
5507/// Yjs RelativePosition serialization format. The `len` parameter is updated with byte length of
5508/// of the output JSON string. This string can be freed using `ystring_destroy`.
5509#[no_mangle]
5510pub unsafe extern "C" fn ysticky_index_to_json(pos: *const YStickyIndex) -> *mut c_char {
5511    let pos = pos.as_ref().unwrap();
5512    let json = match serde_json::to_string(&pos.0) {
5513        Ok(json) => json,
5514        Err(_) => return null_mut(),
5515    };
5516    CString::new(json).unwrap().into_raw()
5517}
5518
5519/// Deserializes `YStickyIndex` from the payload previously serialized using `ysticky_index_to_json`.
5520/// The input `json` parameter is a NULL-terminated UTF-8 encoded string containing a JSON
5521/// compatible with Yjs RelativePosition serialization format.
5522///
5523/// Returns null pointer if deserialization failed.
5524///
5525/// This function DOESN'T release the `json` parameter: it needs to be done manually - if JSON
5526/// string was created using `ysticky_index_to_json` function, it can be freed using `ystring_destroy`.
5527#[no_mangle]
5528pub unsafe extern "C" fn ysticky_index_from_json(json: *const c_char) -> *mut YStickyIndex {
5529    let cstr = CStr::from_ptr(json);
5530    let json = match cstr.to_str() {
5531        Ok(json) => json,
5532        Err(_) => return null_mut(),
5533    };
5534    match serde_json::from_str(json) {
5535        Ok(pos) => Box::into_raw(Box::new(YStickyIndex(pos))),
5536        Err(_) => null_mut(),
5537    }
5538}
5539
5540/// Given `YStickyIndex` and transaction reference, if computes a human-readable index in a
5541/// context of the referenced shared y-type.
5542///
5543/// `out_branch` is getting assigned with a corresponding shared y-type reference.
5544/// `out_index` will be used to store computed human-readable index.
5545#[no_mangle]
5546pub unsafe extern "C" fn ysticky_index_read(
5547    pos: *const YStickyIndex,
5548    txn: *const Transaction,
5549    out_branch: *mut *mut Branch,
5550    out_index: *mut u32,
5551) {
5552    let pos = pos.as_ref().unwrap();
5553    let txn = txn.as_ref().unwrap();
5554
5555    if let Some(abs) = pos.0.get_offset(txn) {
5556        *out_branch = abs.branch.as_ref() as *const Branch as *mut Branch;
5557        *out_index = abs.index as u32;
5558    }
5559}
5560
5561pub type Weak = LinkSource;
5562
5563#[no_mangle]
5564pub unsafe extern "C" fn yweak_destroy(weak: *const Weak) {
5565    drop(Arc::from_raw(weak));
5566}
5567
5568#[no_mangle]
5569pub unsafe extern "C" fn yweak_deref(
5570    map_link: *const Branch,
5571    txn: *const Transaction,
5572) -> *mut YOutput {
5573    assert!(!map_link.is_null());
5574    assert!(!txn.is_null());
5575
5576    let txn = txn.as_ref().unwrap();
5577    let weak: WeakRef<MapRef> = WeakRef::from_raw_branch(map_link);
5578    if let Some(value) = weak.try_deref_value(txn) {
5579        Box::into_raw(Box::new(YOutput::from(value)))
5580    } else {
5581        null_mut()
5582    }
5583}
5584
5585#[no_mangle]
5586pub unsafe extern "C" fn yweak_read(
5587    text_link: *const Branch,
5588    txn: *const Transaction,
5589    out_branch: *mut *mut Branch,
5590    out_start_index: *mut u32,
5591    out_end_index: *mut u32,
5592) {
5593    assert!(!text_link.is_null());
5594    assert!(!txn.is_null());
5595
5596    let txn = txn.as_ref().unwrap();
5597    let weak: WeakRef<BranchPtr> = WeakRef::from_raw_branch(text_link);
5598    if let Some(id) = weak.start_id() {
5599        // Assoc must be After to get the same values back
5600        let start = StickyIndex::from_id(*id, Assoc::After);
5601        assert!(weak.end_id() != None);
5602        let end = StickyIndex::from_id(*weak.end_id().unwrap(), Assoc::After);
5603        if let Some(start_pos) = start.get_offset(txn) {
5604            *out_branch = start_pos.branch.as_ref() as *const Branch as *mut Branch;
5605            *out_start_index = start_pos.index as u32;
5606            if let Some(end_pos) = end.get_offset(txn) {
5607                assert!(*out_branch == end_pos.branch.as_ref() as *const Branch as *mut Branch);
5608                *out_end_index = end_pos.index as u32;
5609            }
5610        }
5611    } else {
5612        assert!(weak.end_id() == None); // both
5613                                        // unforunately no Branch in this case?
5614        *out_start_index = 0; // empty text
5615        *out_end_index = 0; // empty text
5616    }
5617}
5618
5619#[no_mangle]
5620pub unsafe extern "C" fn yweak_iter(
5621    array_link: *const Branch,
5622    txn: *const Transaction,
5623) -> *mut WeakIter {
5624    assert!(!array_link.is_null());
5625    assert!(!txn.is_null());
5626
5627    let txn = txn.as_ref().unwrap();
5628    let weak: WeakRef<ArrayRef> = WeakRef::from_raw_branch(array_link);
5629    let iter: NativeUnquote<'static, Transaction> = std::mem::transmute(weak.unquote(txn));
5630
5631    Box::into_raw(Box::new(WeakIter(iter)))
5632}
5633
5634#[no_mangle]
5635pub unsafe extern "C" fn yweak_iter_destroy(iter: *mut WeakIter) {
5636    drop(Box::from_raw(iter))
5637}
5638
5639#[no_mangle]
5640pub unsafe extern "C" fn yweak_iter_next(iter: *mut WeakIter) -> *mut YOutput {
5641    assert!(!iter.is_null());
5642    let iter = iter.as_mut().unwrap();
5643
5644    if let Some(value) = iter.0.next() {
5645        Box::into_raw(Box::new(YOutput::from(value)))
5646    } else {
5647        null_mut()
5648    }
5649}
5650
5651#[no_mangle]
5652pub unsafe extern "C" fn yweak_string(
5653    text_link: *const Branch,
5654    txn: *const Transaction,
5655) -> *mut c_char {
5656    assert!(!text_link.is_null());
5657    assert!(!txn.is_null());
5658
5659    let txn = txn.as_ref().unwrap();
5660    let weak: WeakRef<TextRef> = WeakRef::from_raw_branch(text_link);
5661
5662    let str = weak.get_string(txn);
5663    CString::new(str).unwrap().into_raw()
5664}
5665
5666#[no_mangle]
5667pub unsafe extern "C" fn yweak_xml_string(
5668    xml_text_link: *const Branch,
5669    txn: *const Transaction,
5670) -> *mut c_char {
5671    assert!(!xml_text_link.is_null());
5672    assert!(!txn.is_null());
5673
5674    let txn = txn.as_ref().unwrap();
5675    let weak: WeakRef<XmlTextRef> = WeakRef::from_raw_branch(xml_text_link);
5676
5677    let str = weak.get_string(txn);
5678    CString::new(str).unwrap().into_raw()
5679}
5680
5681/// Subscribes a given callback function `cb` to changes made by this `YText` instance. Callbacks
5682/// are triggered whenever a `ytransaction_commit` is called.
5683/// Returns a subscription ID which can be then used to unsubscribe this callback by using
5684/// `yunobserve` function.
5685#[no_mangle]
5686pub unsafe extern "C" fn yweak_observe(
5687    weak: *const Branch,
5688    state: *mut c_void,
5689    cb: extern "C" fn(*mut c_void, *const YWeakLinkEvent),
5690) -> *mut Subscription {
5691    assert!(!weak.is_null());
5692
5693    let state = CallbackState::new(state);
5694    let txt: WeakRef<BranchPtr> = WeakRef::from_raw_branch(weak);
5695    let subscription = txt.observe(move |txn, e| {
5696        let e = YWeakLinkEvent::new(e, txn);
5697        cb(state.0, &e as *const YWeakLinkEvent);
5698    });
5699    Box::into_raw(Box::new(subscription))
5700}
5701
5702#[no_mangle]
5703pub unsafe extern "C" fn ymap_link(
5704    map: *const Branch,
5705    txn: *const Transaction,
5706    key: *const c_char,
5707) -> *const Weak {
5708    assert!(!map.is_null());
5709    assert!(!txn.is_null());
5710
5711    let txn = txn.as_ref().unwrap();
5712    let map = MapRef::from_raw_branch(map);
5713    let key = CStr::from_ptr(key).to_str().unwrap();
5714    if let Some(weak) = map.link(txn, key) {
5715        let source = weak.source();
5716        Arc::into_raw(source.clone())
5717    } else {
5718        null()
5719    }
5720}
5721
5722#[no_mangle]
5723pub unsafe extern "C" fn ytext_quote(
5724    text: *const Branch,
5725    txn: *mut Transaction,
5726    start_index: *mut u32,
5727    end_index: *mut u32,
5728    start_exclusive: i8,
5729    end_exclusive: i8,
5730) -> *const Weak {
5731    assert!(!text.is_null());
5732    assert!(!txn.is_null());
5733
5734    let text = TextRef::from_raw_branch(text);
5735    let txn = txn.as_mut().unwrap();
5736    let txn = txn
5737        .as_mut()
5738        .expect("provided transaction was not writeable");
5739
5740    let start_index = start_index.as_ref().cloned();
5741    let end_index = end_index.as_ref().cloned();
5742    let range = ExplicitRange {
5743        start_index,
5744        end_index,
5745        start_exclusive,
5746        end_exclusive,
5747    };
5748    if let Ok(weak) = text.quote(txn, range) {
5749        let source = weak.source();
5750        Arc::into_raw(source.clone())
5751    } else {
5752        null()
5753    }
5754}
5755
5756#[no_mangle]
5757pub unsafe extern "C" fn yarray_quote(
5758    array: *const Branch,
5759    txn: *mut Transaction,
5760    start_index: *mut u32,
5761    end_index: *mut u32,
5762    start_exclusive: i8,
5763    end_exclusive: i8,
5764) -> *const Weak {
5765    assert!(!array.is_null());
5766    assert!(!txn.is_null());
5767
5768    let array = ArrayRef::from_raw_branch(array);
5769    let txn = txn.as_mut().unwrap();
5770    let txn = txn
5771        .as_mut()
5772        .expect("provided transaction was not writeable");
5773
5774    let start_index = start_index.as_ref().cloned();
5775    let end_index = end_index.as_ref().cloned();
5776    let range = ExplicitRange {
5777        start_index,
5778        end_index,
5779        start_exclusive,
5780        end_exclusive,
5781    };
5782    if let Ok(weak) = array.quote(txn, range) {
5783        let source = weak.source();
5784        Arc::into_raw(source.clone())
5785    } else {
5786        null()
5787    }
5788}
5789
5790struct ExplicitRange {
5791    start_index: Option<u32>,
5792    end_index: Option<u32>,
5793    start_exclusive: i8,
5794    end_exclusive: i8,
5795}
5796
5797impl RangeBounds<u32> for ExplicitRange {
5798    fn start_bound(&self) -> Bound<&u32> {
5799        match (&self.start_index, self.start_exclusive) {
5800            (None, _) => Bound::Unbounded,
5801            (Some(i), 0) => Bound::Included(i),
5802            (Some(i), _) => Bound::Excluded(i),
5803        }
5804    }
5805
5806    fn end_bound(&self) -> Bound<&u32> {
5807        match (&self.end_index, self.end_exclusive) {
5808            (None, _) => Bound::Unbounded,
5809            (Some(i), 0) => Bound::Included(i),
5810            (Some(i), _) => Bound::Excluded(i),
5811        }
5812    }
5813}
5814
5815/// A structure representing logical identifier of a specific shared collection.
5816/// Can be obtained by `ybranch_id` executed over alive `Branch`.
5817///
5818/// Use `ybranch_get` to resolve a `Branch` pointer from this branch ID.
5819///
5820/// This structure doesn't need to be destroyed. It's internal pointer reference is valid through
5821/// a lifetime of a document, which collection this branch ID has been created from.
5822#[repr(C)]
5823pub struct YBranchId {
5824    /// If positive: Client ID of a creator of a nested shared type, this identifier points to.
5825    /// If negative: a negated Length of a root-level shared collection name.
5826    pub client_or_len: i64,
5827    pub variant: YBranchIdVariant,
5828}
5829
5830#[repr(C)]
5831pub union YBranchIdVariant {
5832    /// Clock number timestamp when the creator of a nested shared type created it.
5833    pub clock: u32,
5834    /// Pointer to UTF-8 encoded string representing root-level type name. This pointer is valid
5835    /// as long as document - in which scope it was created in - was not destroyed. As usually
5836    /// root-level type names are statically allocated strings, it can also be supplied manually
5837    /// from the outside.
5838    pub name: *const u8,
5839}
5840
5841/// Returns a logical identifier for a given shared collection. That collection must be alive at
5842/// the moment of function call.
5843#[no_mangle]
5844pub unsafe extern "C" fn ybranch_id(branch: *const Branch) -> YBranchId {
5845    let branch = branch.as_ref().unwrap();
5846    match branch.id() {
5847        BranchID::Nested(id) => YBranchId {
5848            client_or_len: id.client.get() as i64,
5849            variant: YBranchIdVariant { clock: id.clock },
5850        },
5851        BranchID::Root(name) => {
5852            let len = -(name.len() as i64);
5853            YBranchId {
5854                client_or_len: len,
5855                variant: YBranchIdVariant {
5856                    name: name.as_ptr(),
5857                },
5858            }
5859        }
5860    }
5861}
5862
5863/// Given a logical identifier, returns a physical pointer to a shared collection.
5864/// Returns null if collection was not found - either because it was not defined or not synchronized
5865/// yet.
5866/// Returned pointer may still point to deleted collection. In such case a subsequent `ybranch_alive`
5867/// function call is required.
5868#[no_mangle]
5869pub unsafe extern "C" fn ybranch_get(
5870    branch_id: *const YBranchId,
5871    txn: *mut Transaction,
5872) -> *mut Branch {
5873    let txn = txn.as_ref().unwrap();
5874    let branch_id = branch_id.as_ref().unwrap();
5875    let client_or_len = branch_id.client_or_len;
5876    let ptr = if client_or_len >= 0 {
5877        BranchID::get_nested(
5878            txn,
5879            &ID::new(ClientID::new(client_or_len as u64), branch_id.variant.clock),
5880        )
5881    } else {
5882        let name = std::slice::from_raw_parts(branch_id.variant.name, (-client_or_len) as usize);
5883        BranchID::get_root(txn, std::str::from_utf8_unchecked(name))
5884    };
5885
5886    match ptr {
5887        None => null_mut(),
5888        Some(branch_ptr) => branch_ptr.into_raw_branch(),
5889    }
5890}
5891
5892/// Check if current branch is still alive (returns `Y_TRUE`, otherwise `Y_FALSE`).
5893/// If it was deleted, this branch pointer is no longer a valid pointer and cannot be used to
5894/// execute any functions using it.
5895#[no_mangle]
5896pub unsafe extern "C" fn ybranch_alive(branch: *mut Branch) -> u8 {
5897    if branch.is_null() {
5898        Y_FALSE
5899    } else {
5900        let branch = BranchPtr::from_raw_branch(branch);
5901        if branch.is_deleted() {
5902            Y_FALSE
5903        } else {
5904            Y_TRUE
5905        }
5906    }
5907}
5908
5909/// Returns a UTF-8 encoded, NULL-terminated JSON string representation of the current branch
5910/// contents. Once no longer needed, this string must be explicitly deallocated by user using
5911/// `ystring_destroy`.
5912///
5913/// If branch type couldn't be resolved (which usually happens for root-level types that were not
5914/// initialized locally) or doesn't have JSON representation a NULL pointer can be returned.
5915#[no_mangle]
5916pub unsafe extern "C" fn ybranch_json(branch: *mut Branch, txn: *mut Transaction) -> *mut c_char {
5917    if branch.is_null() {
5918        std::ptr::null_mut()
5919    } else {
5920        let txn = txn.as_ref().unwrap();
5921        let branch_ref = BranchPtr::from_raw_branch(branch);
5922        let any = match branch_ref.type_ref() {
5923            TypeRef::Array => ArrayRef::from_raw_branch(branch).to_json(txn),
5924            TypeRef::Map => MapRef::from_raw_branch(branch).to_json(txn),
5925            TypeRef::Text => TextRef::from_raw_branch(branch).get_string(txn).into(),
5926            TypeRef::XmlElement(_) => XmlElementRef::from_raw_branch(branch)
5927                .get_string(txn)
5928                .into(),
5929            TypeRef::XmlFragment => XmlFragmentRef::from_raw_branch(branch)
5930                .get_string(txn)
5931                .into(),
5932            TypeRef::XmlText => XmlTextRef::from_raw_branch(branch).get_string(txn).into(),
5933            TypeRef::SubDoc | TypeRef::XmlHook | TypeRef::WeakLink(_) | TypeRef::Undefined => {
5934                return std::ptr::null_mut()
5935            }
5936        };
5937        let json = match serde_json::to_string(&any) {
5938            Ok(json) => json,
5939            Err(_) => return std::ptr::null_mut(),
5940        };
5941        CString::new(json).unwrap().into_raw()
5942    }
5943}