yrs/
lib.rs

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