Something went wrong. Try again.
Go bindings to the Rust Yrs library that implements the Yjs CRDT
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340package ygo
/*#include "libyrs.h"#include <stdlib.h>*/import "C"import ( "encoding/json" "fmt" "unsafe")
// Array represents a collaborative array type.type Array struct { branch *C.Branch}
// GetArray retrieves or creates a root-level YArray with the given name.func (d *Doc) GetArray(name string) (*Array, error) { if d.ptr == nil { return nil, ErrNilDocument } cName := C.CString(name) defer C.free(unsafe.Pointer(cName))
branch := C.yarray(d.ptr, cName) if branch == nil { return nil, fmt.Errorf("failed to get or create array field %q", name) }
a := &Array{branch: branch} return a, nil}
// Destroy releases resources.func (a *Array) Destroy() {}
// Len returns the number of elements.// Panics if the Array is nil (like len() on a nil slice).func (a *Array) Len() uint32 { if a.branch == nil { panic("ygo: Array.Len called on nil Array") } return uint32(C.yarray_len(a.branch))}
// Get returns the element at index.func (a *Array) Get(txn *Transaction, index uint32) (*Output, error) { if a.branch == nil { return nil, ErrNilBranch } if txn == nil || txn.ptr == nil { return nil, ErrNilTransaction } ptr := C.yarray_get(a.branch, txn.ptr, C.uint32_t(index)) if ptr == nil { return nil, ErrInvalidIndex } return &Output{ptr: ptr}, nil}
// InsertRange inserts multiple items starting at index.func (a *Array) InsertRange(txn *Transaction, index uint32, items []Input) error { if a.branch == nil { return ErrNilBranch } if txn == nil || txn.ptr == nil { return ErrNilTransaction } if !txn.IsWriteable() { return ErrNotWriteable } if len(items) == 0 { return nil }
// Convert items to C array cInputs := make([]C.YInput, len(items)) for i, item := range items { cInputs[i] = item.cInput }
C.yarray_insert_range(a.branch, txn.ptr, C.uint32_t(index), &cInputs[0], C.uint32_t(len(items))) return nil}
// Push adds an item to the end.func (a *Array) Push(txn *Transaction, item Input) error { return a.InsertRange(txn, a.Len(), []Input{item})}
// RemoveRange removes elements starting at index.func (a *Array) RemoveRange(txn *Transaction, index, length uint32) error { if a.branch == nil { return ErrNilBranch } if txn == nil || txn.ptr == nil { return ErrNilTransaction } if !txn.IsWriteable() { return ErrNotWriteable } C.yarray_remove_range(a.branch, txn.ptr, C.uint32_t(index), C.uint32_t(length)) return nil}
// Iter returns an iterator over the array.func (a *Array) Iter(txn *Transaction) (*ArrayIter, error) { if a.branch == nil { return nil, ErrNilBranch } if txn == nil || txn.ptr == nil { return nil, ErrNilTransaction } ptr := C.yarray_iter(a.branch, txn.ptr) if ptr == nil { return nil, fmt.Errorf("failed to create array iterator") } return &ArrayIter{ptr: ptr}, nil}
// ForEach iterates over all elements in the array, calling fn for each element.// The index is passed as the first argument to fn.// If fn returns an error, iteration stops and the error is returned.// The Output is automatically destroyed after fn returns.func (a *Array) ForEach(txn *Transaction, fn func(index uint32, value *Output) error) error { iter, err := a.Iter(txn) if err != nil { return err } defer iter.Destroy()
var index uint32 for { out, err := iter.Next() if err != nil { return err } if out == nil { break }
if err := fn(index, out); err != nil { out.Destroy() return err } out.Destroy() index++ } return nil}
// ArrayIter iterates over array elements.type ArrayIter struct { ptr *C.YArrayIter}
// Destroy releases iterator resources.func (it *ArrayIter) Destroy() { if it.ptr != nil { C.yarray_iter_destroy(it.ptr) it.ptr = nil }}
// Next returns the next element. Returns nil, nil when iteration is complete.func (it *ArrayIter) Next() (*Output, error) { if it.ptr == nil { return nil, ErrIteratorExhausted } ptr := C.yarray_iter_next(it.ptr) if ptr == nil { return nil, nil // End of iteration } return &Output{ptr: ptr}, nil}
// Branch returns the underlying branch pointer.func (a *Array) Branch() unsafe.Pointer { return unsafe.Pointer(a.branch)}
// MarshalJSONWithTxn implements JSON marshaling for Array.// Returns a JSON array with all elements marshaled to JSON.func (a *Array) MarshalJSONWithTxn(txn *Transaction) ([]byte, error) { if a.branch == nil { return nil, ErrNilBranch } if txn == nil || txn.ptr == nil { return nil, ErrNilTransaction }
// Collect all elements var elements []interface{}
iter, err := a.Iter(txn) if err != nil { return nil, err } defer iter.Destroy()
for { out, err := iter.Next() if err != nil { return nil, err } if out == nil { break }
// Convert value to JSON-compatible type val, err := a.valueToJSON(out, txn) if err != nil { out.Destroy() return nil, err }
elements = append(elements, val) out.Destroy() }
return json.Marshal(elements)}
// valueToJSON converts an Output value to a JSON-compatible Go value.// For primitive types, uses Output methods. For shared types, recursively marshals.func (a *Array) valueToJSON(out *Output, txn *Transaction) (interface{}, error) { if out == nil || out.ptr == nil { return nil, nil }
switch out.Tag() { // Primitive JSON types - use Output methods case TagJSONNull: return nil, nil case TagJSONBool: if v, ok := out.Bool(); ok { return v, nil } return nil, fmt.Errorf("failed to read boolean") case TagJSONNum: if v, ok := out.Float(); ok { return v, nil } return nil, fmt.Errorf("failed to read float") case TagJSONInt: if v, ok := out.Int(); ok { return v, nil } return nil, fmt.Errorf("failed to read int") case TagJSONStr: if v, ok := out.String(); ok { return v, nil } return nil, fmt.Errorf("failed to read string") case TagJSONBuf: if v, ok := out.Binary(); ok { return v, nil } return nil, fmt.Errorf("failed to read binary")
// JSON array - parsed JSON array case TagJSONArr: return out.convertToJSONArray()
// Shared types - recursively marshal case TagMap: branch := C.youtput_read_ymap(out.ptr) if branch == nil { return nil, fmt.Errorf("failed to read ymap from output") } m := &Map{branch: branch} // Create a temporary map with just the branch pointer // We need to get data from it data := make(map[string]interface{}) iter, err := m.Iter(txn) if err != nil { return nil, err } defer iter.Destroy() for { entry, err := iter.Next() if err != nil { return nil, err } if entry == nil { break } val, err := m.valueToJSON(entry.Value, txn) if err != nil { entry.Value.Destroy() return nil, fmt.Errorf("key %q: %w", entry.Key, err) } data[entry.Key] = val entry.Value.Destroy() } return data, nil case TagArray: branch := C.youtput_read_yarray(out.ptr) if branch == nil { return nil, fmt.Errorf("failed to read yarray from output") } arr := &Array{branch: branch} var elements []interface{} iter, err := arr.Iter(txn) if err != nil { return nil, err } defer iter.Destroy() for { el, err := iter.Next() if err != nil { return nil, err } if el == nil { break } val, err := arr.valueToJSON(el, txn) if err != nil { el.Destroy() return nil, err } elements = append(elements, val) el.Destroy() } return elements, nil case TagText, TagXmlElem, TagXmlText, TagXmlFrag: // XML types not supported in array context return nil, fmt.Errorf("XML types not supported in array JSON marshaling: %v", out.Tag()) case TagDoc: return nil, fmt.Errorf("nested documents not supported in JSON marshaling") case TagWeakLink, TagUndefined, TagJSONUndef: return nil, nil default: return nil, fmt.Errorf("unsupported type tag: %v", out.Tag()) }}