MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1const std = @import("std");
2
3pub const yyjson = @cImport({
4 @cInclude("yyjson.h");
5});
6
7pub const JsonError = error{
8 ParseError,
9 OutOfMemory,
10 InvalidType,
11 KeyNotFound,
12 IoError,
13};
14
15pub const JsonDoc = struct {
16 doc: *yyjson.yyjson_doc,
17
18 pub fn parse(data: []const u8) !JsonDoc {
19 const doc = yyjson.yyjson_read(data.ptr, data.len, 0);
20 if (doc == null) return error.ParseError;
21 return JsonDoc{ .doc = doc.? };
22 }
23
24 pub fn parseFile(path: [:0]const u8) !JsonDoc {
25 const doc = yyjson.yyjson_read_file(path.ptr, 0, null, null);
26 if (doc == null) return error.ParseError;
27 return JsonDoc{ .doc = doc.? };
28 }
29
30 pub fn deinit(self: *JsonDoc) void {
31 yyjson.yyjson_doc_free(self.doc);
32 }
33
34 pub fn root(self: *JsonDoc) JsonValue {
35 return JsonValue{ .val = yyjson.yyjson_doc_get_root(self.doc).? };
36 }
37};
38
39pub const JsonValue = struct {
40 val: *yyjson.yyjson_val,
41
42 pub fn getString(self: JsonValue, key: [:0]const u8) ?[]const u8 {
43 const obj = yyjson.yyjson_obj_get(self.val, key.ptr) orelse return null;
44 if (!yyjson.yyjson_is_str(obj)) return null;
45 const ptr = yyjson.yyjson_get_str(obj) orelse return null;
46 const len = yyjson.yyjson_get_len(obj);
47 return ptr[0..len];
48 }
49
50 pub fn getInt(self: JsonValue, key: [:0]const u8) ?i64 {
51 const obj = yyjson.yyjson_obj_get(self.val, key.ptr) orelse return null;
52 if (!yyjson.yyjson_is_int(obj)) return null;
53 return yyjson.yyjson_get_sint(obj);
54 }
55
56 pub fn getUint(self: JsonValue, key: [:0]const u8) ?u64 {
57 const obj = yyjson.yyjson_obj_get(self.val, key.ptr) orelse return null;
58 if (!yyjson.yyjson_is_uint(obj)) return null;
59 return yyjson.yyjson_get_uint(obj);
60 }
61
62 pub fn getDouble(self: JsonValue, key: [:0]const u8) ?f64 {
63 const obj = yyjson.yyjson_obj_get(self.val, key.ptr) orelse return null;
64 if (!yyjson.yyjson_is_real(obj)) return null;
65 return yyjson.yyjson_get_real(obj);
66 }
67
68 pub fn getBool(self: JsonValue, key: [:0]const u8) ?bool {
69 const obj = yyjson.yyjson_obj_get(self.val, key.ptr) orelse return null;
70 if (!yyjson.yyjson_is_bool(obj)) return null;
71 return yyjson.yyjson_get_bool(obj);
72 }
73
74 pub fn getObject(self: JsonValue, key: [:0]const u8) ?JsonValue {
75 const obj = yyjson.yyjson_obj_get(self.val, key.ptr) orelse return null;
76 if (!yyjson.yyjson_is_obj(obj)) return null;
77 return JsonValue{ .val = obj };
78 }
79
80 pub fn getArray(self: JsonValue, key: [:0]const u8) ?JsonValue {
81 const obj = yyjson.yyjson_obj_get(self.val, key.ptr) orelse return null;
82 if (!yyjson.yyjson_is_arr(obj)) return null;
83 return JsonValue{ .val = obj };
84 }
85
86 pub fn isNull(self: JsonValue) bool {
87 return yyjson.yyjson_is_null(self.val);
88 }
89
90 pub fn isArray(self: JsonValue) bool {
91 return yyjson.yyjson_is_arr(self.val);
92 }
93
94 pub fn isObject(self: JsonValue) bool {
95 return yyjson.yyjson_is_obj(self.val);
96 }
97
98 pub fn arrayLen(self: JsonValue) usize {
99 return yyjson.yyjson_arr_size(self.val);
100 }
101
102 pub fn arrayGet(self: JsonValue, index: usize) ?JsonValue {
103 const elem = yyjson.yyjson_arr_get(self.val, index) orelse return null;
104 return JsonValue{ .val = elem };
105 }
106
107 pub fn asString(self: JsonValue) ?[]const u8 {
108 if (!yyjson.yyjson_is_str(self.val)) return null;
109 const ptr = yyjson.yyjson_get_str(self.val) orelse return null;
110 const len = yyjson.yyjson_get_len(self.val);
111 return ptr[0..len];
112 }
113
114 pub const ObjectIterator = struct {
115 iter: yyjson.yyjson_obj_iter,
116
117 pub fn next(self: *ObjectIterator) ?struct { key: []const u8, value: JsonValue } {
118 const key_val = yyjson.yyjson_obj_iter_next(&self.iter) orelse return null;
119 const val = yyjson.yyjson_obj_iter_get_val(key_val) orelse return null;
120
121 const key_ptr = yyjson.yyjson_get_str(key_val) orelse return null;
122 const key_len = yyjson.yyjson_get_len(key_val);
123
124 return .{
125 .key = key_ptr[0..key_len],
126 .value = JsonValue{ .val = val },
127 };
128 }
129
130 pub fn deinit(_: *ObjectIterator) void {}
131 };
132
133 pub fn objectIterator(self: JsonValue) ?ObjectIterator {
134 if (!yyjson.yyjson_is_obj(self.val)) return null;
135 var iter: yyjson.yyjson_obj_iter = undefined;
136 if (!yyjson.yyjson_obj_iter_init(self.val, &iter)) return null;
137 return ObjectIterator{ .iter = iter };
138 }
139
140 pub const ArrayIterator = struct {
141 iter: yyjson.yyjson_arr_iter,
142
143 pub fn next(self: *ArrayIterator) ?JsonValue {
144 const val = yyjson.yyjson_arr_iter_next(&self.iter) orelse return null;
145 return JsonValue{ .val = val };
146 }
147
148 pub fn deinit(_: *ArrayIterator) void {}
149 };
150
151 pub fn arrayIterator(self: JsonValue) ?ArrayIterator {
152 if (!yyjson.yyjson_is_arr(self.val)) return null;
153 var iter: yyjson.yyjson_arr_iter = undefined;
154 if (!yyjson.yyjson_arr_iter_init(self.val, &iter)) return null;
155 return ArrayIterator{ .iter = iter };
156 }
157};
158
159pub const JsonWriter = struct {
160 doc: *yyjson.yyjson_mut_doc,
161
162 pub fn init() !JsonWriter {
163 const doc = yyjson.yyjson_mut_doc_new(null);
164 if (doc == null) return error.OutOfMemory;
165 return JsonWriter{ .doc = doc.? };
166 }
167
168 pub fn deinit(self: *JsonWriter) void {
169 yyjson.yyjson_mut_doc_free(self.doc);
170 }
171
172 pub fn createObject(self: *JsonWriter) *yyjson.yyjson_mut_val {
173 return yyjson.yyjson_mut_obj(self.doc).?;
174 }
175
176 pub fn createArray(self: *JsonWriter) *yyjson.yyjson_mut_val {
177 return yyjson.yyjson_mut_arr(self.doc).?;
178 }
179
180 pub fn createString(self: *JsonWriter, str: []const u8) *yyjson.yyjson_mut_val {
181 return yyjson.yyjson_mut_strncpy(self.doc, str.ptr, str.len).?;
182 }
183
184 pub fn createInt(self: *JsonWriter, val: i64) *yyjson.yyjson_mut_val {
185 return yyjson.yyjson_mut_sint(self.doc, val).?;
186 }
187
188 pub fn createUint(self: *JsonWriter, val: u64) *yyjson.yyjson_mut_val {
189 return yyjson.yyjson_mut_uint(self.doc, val).?;
190 }
191
192 pub fn createBool(self: *JsonWriter, val: bool) *yyjson.yyjson_mut_val {
193 return yyjson.yyjson_mut_bool(self.doc, val).?;
194 }
195
196 pub fn createReal(self: *JsonWriter, val: f64) *yyjson.yyjson_mut_val {
197 return yyjson.yyjson_mut_real(self.doc, val).?;
198 }
199
200 pub fn createNull(self: *JsonWriter) *yyjson.yyjson_mut_val {
201 return yyjson.yyjson_mut_null(self.doc).?;
202 }
203
204 pub fn objectAdd(self: *JsonWriter, obj: *yyjson.yyjson_mut_val, key: []const u8, val: *yyjson.yyjson_mut_val) void {
205 const key_val = yyjson.yyjson_mut_strncpy(self.doc, key.ptr, key.len);
206 _ = yyjson.yyjson_mut_obj_add(obj, key_val, val);
207 }
208
209 pub fn arrayAppend(_: *JsonWriter, arr: *yyjson.yyjson_mut_val, val: *yyjson.yyjson_mut_val) void {
210 _ = yyjson.yyjson_mut_arr_append(arr, val);
211 }
212
213 pub fn setRoot(self: *JsonWriter, val: *yyjson.yyjson_mut_val) void {
214 yyjson.yyjson_mut_doc_set_root(self.doc, val);
215 }
216
217 pub fn write(self: *JsonWriter, allocator: std.mem.Allocator) ![]u8 {
218 var len: usize = 0;
219 const ptr = yyjson.yyjson_mut_write(self.doc, yyjson.YYJSON_WRITE_PRETTY_TWO_SPACES, &len);
220 if (ptr == null) return error.OutOfMemory;
221 defer std.c.free(ptr);
222
223 const result = try allocator.alloc(u8, len);
224 @memcpy(result, ptr[0..len]);
225 return result;
226 }
227
228 pub fn writeToFile(self: *JsonWriter, path: [:0]const u8) !void {
229 const success = yyjson.yyjson_mut_write_file(path.ptr, self.doc, yyjson.YYJSON_WRITE_PRETTY_TWO_SPACES, null, null);
230 if (!success) return error.IoError;
231 }
232};
233
234pub const PackageJson = struct {
235 name: []const u8,
236 version: []const u8,
237 dependencies: std.StringHashMap([]const u8),
238 dev_dependencies: std.StringHashMap([]const u8),
239 peer_dependencies: std.StringHashMap([]const u8),
240 optional_dependencies: std.StringHashMap([]const u8),
241 trusted_dependencies: std.StringHashMap(void),
242
243 pub fn parse(allocator: std.mem.Allocator, path: [:0]const u8) !PackageJson {
244 var doc = try JsonDoc.parseFile(path);
245 defer doc.deinit();
246
247 const root_val = doc.root();
248
249 var pkg = PackageJson{
250 .name = "",
251 .version = "",
252 .dependencies = std.StringHashMap([]const u8).init(allocator),
253 .dev_dependencies = std.StringHashMap([]const u8).init(allocator),
254 .peer_dependencies = std.StringHashMap([]const u8).init(allocator),
255 .optional_dependencies = std.StringHashMap([]const u8).init(allocator),
256 .trusted_dependencies = std.StringHashMap(void).init(allocator),
257 };
258
259 if (root_val.getString("name")) |s| {
260 pkg.name = try allocator.dupe(u8, s);
261 }
262
263 if (root_val.getString("version")) |s| {
264 pkg.version = try allocator.dupe(u8, s);
265 }
266
267 try parseDeps(allocator, root_val, "dependencies", &pkg.dependencies);
268 try parseDeps(allocator, root_val, "devDependencies", &pkg.dev_dependencies);
269 try parseDeps(allocator, root_val, "peerDependencies", &pkg.peer_dependencies);
270 try parseDeps(allocator, root_val, "optionalDependencies", &pkg.optional_dependencies);
271
272 if (root_val.getArray("trustedDependencies")) |arr| {
273 for (0..arr.arrayLen()) |i| {
274 const name = (arr.arrayGet(i) orelse continue).asString() orelse continue;
275 try pkg.trusted_dependencies.put(try allocator.dupe(u8, name), {});
276 }
277 }
278
279 return pkg;
280 }
281
282 fn parseDeps(
283 allocator: std.mem.Allocator,
284 root_val: JsonValue,
285 key: [:0]const u8,
286 map: *std.StringHashMap([]const u8),
287 ) !void {
288 if (root_val.getObject(key)) |deps| {
289 var iter = deps.objectIterator() orelse return;
290 defer iter.deinit();
291 while (iter.next()) |entry| {
292 const version = entry.value.asString() orelse continue;
293 try map.put(try allocator.dupe(u8, entry.key), try allocator.dupe(u8, version));
294 }
295 }
296 }
297
298 pub fn deinit(self: *PackageJson, allocator: std.mem.Allocator) void {
299 if (self.name.len > 0) allocator.free(self.name);
300 if (self.version.len > 0) allocator.free(self.version);
301
302 var iter = self.dependencies.iterator();
303 while (iter.next()) |entry| {
304 allocator.free(entry.key_ptr.*);
305 allocator.free(entry.value_ptr.*);
306 }
307 self.dependencies.deinit();
308
309 iter = self.dev_dependencies.iterator();
310 while (iter.next()) |entry| {
311 allocator.free(entry.key_ptr.*);
312 allocator.free(entry.value_ptr.*);
313 }
314 self.dev_dependencies.deinit();
315
316 iter = self.peer_dependencies.iterator();
317 while (iter.next()) |entry| {
318 allocator.free(entry.key_ptr.*);
319 allocator.free(entry.value_ptr.*);
320 }
321 self.peer_dependencies.deinit();
322
323 iter = self.optional_dependencies.iterator();
324 while (iter.next()) |entry| {
325 allocator.free(entry.key_ptr.*);
326 allocator.free(entry.value_ptr.*);
327 }
328 self.optional_dependencies.deinit();
329
330 var trusted_iter = self.trusted_dependencies.keyIterator();
331 while (trusted_iter.next()) |key| {
332 allocator.free(key.*);
333 }
334 self.trusted_dependencies.deinit();
335 }
336};