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