Skip to main content

yrs/
lib.rs

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