input.go

  1package ygo
  2
  3/*
  4#include "libyrs.h"
  5#include <stdlib.h>
  6*/
  7import "C"
  8import (
  9	"encoding/json"
 10	"fmt"
 11	"unsafe"
 12)
 13
 14// Input represents a value to be inserted into Yjs types.
 15// This is a wrapper around C YInput with proper memory management.
 16type Input struct {
 17	cInput C.YInput
 18}
 19
 20// Null creates a null input value.
 21func Null() Input {
 22	return Input{cInput: C.yinput_null()}
 23}
 24
 25// Undefined creates an undefined input value.
 26func Undefined() Input {
 27	return Input{cInput: C.yinput_undefined()}
 28}
 29
 30// Bool creates a boolean input value.
 31func Bool(v bool) Input {
 32	var flag C.uint8_t
 33	if v {
 34		flag = C.Y_TRUE
 35	}
 36	return Input{cInput: C.yinput_bool(flag)}
 37}
 38
 39// Float creates a 64-bit floating point input value.
 40func Float(v float64) Input {
 41	return Input{cInput: C.yinput_float(C.double(v))}
 42}
 43
 44// Int creates a 64-bit integer input value.
 45func Int(v int64) Input {
 46	return Input{cInput: C.yinput_long(C.int64_t(v))}
 47}
 48
 49// String creates a string input value.
 50func String(v string) Input {
 51	cStr := C.CString(v)
 52	return Input{cInput: C.yinput_string(cStr)}
 53}
 54
 55// JSON creates a JSON-embedded input value from any JSON-marshalable Go value.
 56// Returns (Input, error) - the error is non-nil if marshaling fails.
 57func JSON(v interface{}) (Input, error) {
 58	data, err := json.Marshal(v)
 59	if err != nil {
 60		return Input{}, fmt.Errorf("failed to marshal JSON: %w", err)
 61	}
 62	cStr := C.CString(string(data))
 63	return Input{cInput: C.yinput_json(cStr)}, nil
 64}
 65
 66// Binary creates a binary input value.
 67func Binary(v []byte) Input {
 68	if len(v) == 0 {
 69		var ptr *C.char
 70		return Input{cInput: C.yinput_binary(ptr, 0)}
 71	}
 72	return Input{cInput: C.yinput_binary((*C.char)(unsafe.Pointer(&v[0])), C.uint32_t(len(v)))}
 73}
 74
 75// JSONArray creates a JSON array input from a slice of Inputs.
 76func JSONArray(items []Input) Input {
 77	if len(items) == 0 {
 78		var ptr *C.YInput
 79		return Input{cInput: C.yinput_json_array(ptr, 0)}
 80	}
 81	// Convert slice of Inputs to raw C array
 82	cInputs := make([]C.YInput, len(items))
 83	for i, item := range items {
 84		cInputs[i] = item.cInput
 85	}
 86	return Input{cInput: C.yinput_json_array(&cInputs[0], C.uint32_t(len(items)))}
 87}
 88
 89// JSONMap creates a JSON map input from key-value pairs.
 90// Returns (Input, error) - the error is non-nil if keys and values have different lengths.
 91// NOTE: This creates C strings that will be leaked. The yffi library stores
 92// pointers to these strings in the YInput struct but doesn't take ownership.
 93// For production use, proper memory management needs to be implemented.
 94func JSONMap(keys []string, values []Input) (Input, error) {
 95	if len(keys) != len(values) {
 96		return Input{}, fmt.Errorf("keys and values must have same length: got %d keys and %d values", len(keys), len(values))
 97	}
 98	if len(keys) == 0 {
 99		var keysPtr **C.char
100		var valsPtr *C.YInput
101		return Input{cInput: C.yinput_json_map(keysPtr, valsPtr, 0)}, nil
102	}
103
104	// Convert keys to C strings - these are NOT freed because
105	// yinput_json_map stores the pointers in the returned struct
106	cKeys := make([]*C.char, len(keys))
107	for i, k := range keys {
108		cKeys[i] = C.CString(k)
109	}
110
111	// Convert values
112	cVals := make([]C.YInput, len(values))
113	for i, v := range values {
114		cVals[i] = v.cInput
115	}
116
117	result := Input{cInput: C.yinput_json_map(&cKeys[0], &cVals[0], C.uint32_t(len(keys)))}
118
119	// Note: We intentionally don't free cKeys here because yffi stores
120	// the pointers in the YInput struct and uses them later.
121	// TODO: Implement proper memory management for Input allocations
122
123	return result, nil
124}
125
126// YArray creates a YArray shared type input.
127func YArray(items []Input) Input {
128	if len(items) == 0 {
129		var ptr *C.YInput
130		return Input{cInput: C.yinput_yarray(ptr, 0)}
131	}
132	cInputs := make([]C.YInput, len(items))
133	for i, item := range items {
134		cInputs[i] = item.cInput
135	}
136	return Input{cInput: C.yinput_yarray(&cInputs[0], C.uint32_t(len(items)))}
137}
138
139// YMap creates a YMap shared type input.
140// Returns (Input, error) - the error is non-nil if keys and values have different lengths.
141// NOTE: This creates C strings that will be leaked. The yffi library stores
142// pointers to these strings in the YInput struct but doesn't take ownership.
143// For production use, proper memory management needs to be implemented.
144func YMap(keys []string, values []Input) (Input, error) {
145	if len(keys) != len(values) {
146		return Input{}, fmt.Errorf("keys and values must have same length: got %d keys and %d values", len(keys), len(values))
147	}
148	if len(keys) == 0 {
149		var keysPtr **C.char
150		var valsPtr *C.YInput
151		return Input{cInput: C.yinput_ymap(keysPtr, valsPtr, 0)}, nil
152	}
153
154	cKeys := make([]*C.char, len(keys))
155	for i, k := range keys {
156		cKeys[i] = C.CString(k)
157	}
158
159	cVals := make([]C.YInput, len(values))
160	for i, v := range values {
161		cVals[i] = v.cInput
162	}
163
164	result := Input{cInput: C.yinput_ymap(&cKeys[0], &cVals[0], C.uint32_t(len(keys)))}
165
166	// Note: We intentionally don't free cKeys here because yffi stores
167	// the pointers in the YInput struct and uses them later.
168	// TODO: Implement proper memory management for Input allocations
169
170	return result, nil
171}
172
173// MapInput creates a YMap shared type input from a Go map.
174// Usage: ygo.MapInput(map[string]ygo.Input{"name": ygo.String("Alice"), "age": ygo.Int(30)})
175//
176// Note: The iteration order of Go maps is not guaranteed, so the resulting
177// YMap may have keys in any order.
178func MapInput(items map[string]Input) (Input, error) {
179	if len(items) == 0 {
180		return YMap(nil, nil)
181	}
182
183	// Extract keys and values
184	keys := make([]string, 0, len(items))
185	values := make([]Input, 0, len(items))
186	for k, v := range items {
187		keys = append(keys, k)
188		values = append(values, v)
189	}
190
191	return YMap(keys, values)
192}
193
194// JSONObject creates a JSON map input from a Go map.
195// Usage: ygo.JSONObject(map[string]ygo.Input{"name": ygo.String("Alice"), "age": ygo.Int(30)})
196//
197// Note: The iteration order of Go maps is not guaranteed, so the resulting
198// JSON object may have keys in any order.
199func JSONObject(items map[string]Input) (Input, error) {
200	if len(items) == 0 {
201		return JSONMap(nil, nil)
202	}
203
204	keys := make([]string, 0, len(items))
205	values := make([]Input, 0, len(items))
206	for k, v := range items {
207		keys = append(keys, k)
208		values = append(values, v)
209	}
210
211	return JSONMap(keys, values)
212}
213
214// YText creates a YText shared type input with initial content.
215func YText(initial string) Input {
216	cStr := C.CString(initial)
217	return Input{cInput: C.yinput_ytext(cStr)}
218}
219
220// YXmlElement creates an XML element input with given tag.
221func YXmlElement(tag string) Input {
222	cStr := C.CString(tag)
223	return Input{cInput: C.yinput_yxmlelem(cStr)}
224}
225
226// YXmlText creates an XML text input with initial content.
227func YXmlText(initial string) Input {
228	cStr := C.CString(initial)
229	return Input{cInput: C.yinput_yxmltext(cStr)}
230}