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}