@voxgig/sdkgen 4.8.3 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/package.json +1 -1
- package/project/.sdk/model/target/zig.aon +9 -0
- package/project/.sdk/src/cmp/zig/Package_zig.ts +15 -11
- package/project/.sdk/src/cmp/zig/fragment/Entity.fragment.zig +3 -3
- package/project/.sdk/src/cmp/zig/fragment/EntityListOp.fragment.zig +3 -3
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +3 -3
- package/project/.sdk/src/cmp/zig/utility_zig.ts +50 -1
- package/project/.sdk/tm/c/tests/vendor/omni/json.c +2 -2
- package/project/.sdk/tm/c/tests/vendor/omni/omni.h +2 -2
- package/project/.sdk/tm/c/tests/vendor/omni/runner.c +2 -2
- package/project/.sdk/tm/c/tests/vendor/omni/util.c +2 -2
- package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/json.clj +2 -2
- package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/runner.clj +2 -2
- package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/util.clj +2 -2
- package/project/.sdk/tm/cpp/test/vendor/omni/json.hpp +2 -2
- package/project/.sdk/tm/cpp/test/vendor/omni/omni.hpp +2 -2
- package/project/.sdk/tm/cpp/test/vendor/omni/util.hpp +2 -2
- package/project/.sdk/tm/csharp/test/vendor/omni/Runner.cs +2 -2
- package/project/.sdk/tm/csharp/test/vendor/omni/Util.cs +2 -2
- package/project/.sdk/tm/csharp/utility/struct/Struct.cs +1 -1
- package/project/.sdk/tm/dart/test/vendor/omni/omni.dart +2 -2
- package/project/.sdk/tm/dart/test/vendor/omni/runner.dart +2 -2
- package/project/.sdk/tm/dart/test/vendor/omni/util.dart +2 -2
- package/project/.sdk/tm/elixir/test/vendor/omni/json.ex +2 -2
- package/project/.sdk/tm/elixir/test/vendor/omni/runner.ex +2 -2
- package/project/.sdk/tm/elixir/test/vendor/omni/util.ex +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugin/capability.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/catalog.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/config.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/depend.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/env.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/export.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/graph.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/host.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/order.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/point.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/ref.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/resolve.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/types.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/util.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/version.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugins/aws/aws.go +4 -4
- package/project/.sdk/tm/go/feature/secrets/plugins/aws/sigv4.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/azuresecrets/azuresecrets.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/boru/boru.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/doppler/doppler.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/gcpsecrets/gcpsecrets.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/hashicorp/hashicorp.go +4 -5
- package/project/.sdk/tm/go/feature/secrets/plugins/httpjson/httpjson.go +3 -3
- package/project/.sdk/tm/go/feature/secrets/plugins/infisical/infisical.go +3 -4
- package/project/.sdk/tm/go/feature/secrets/plugins/onepassword/onepassword.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/secretspec/secretspec.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/sekreto/addr.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/sekreto/providers.go +5 -2
- package/project/.sdk/tm/go/feature/secrets/sekreto/sekreto.go +31 -2
- package/project/.sdk/tm/go/test/omni/omni.go +8 -3
- package/project/.sdk/tm/go/test/omni/util.go +25 -4
- package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +1 -1
- package/project/.sdk/tm/java/test/vendor/omni/Json.java +2 -2
- package/project/.sdk/tm/java/test/vendor/omni/Runner.java +2 -2
- package/project/.sdk/tm/java/test/vendor/omni/Util.java +2 -2
- package/project/.sdk/tm/java/utility/struct/Struct.java +1 -1
- package/project/.sdk/tm/js/src/utility/StructUtility.js +2 -2
- package/project/.sdk/tm/js/test/vendor/omni/index.js +1 -1
- package/project/.sdk/tm/js/test/vendor/omni/runner.js +18 -3
- package/project/.sdk/tm/js/test/vendor/omni/util.js +25 -8
- package/project/.sdk/tm/kotlin/test/vendor/omni/Json.kt +2 -2
- package/project/.sdk/tm/kotlin/test/vendor/omni/Runner.kt +2 -2
- package/project/.sdk/tm/kotlin/test/vendor/omni/Util.kt +2 -2
- package/project/.sdk/tm/kotlin/utility/struct/Struct.kt +1 -1
- package/project/.sdk/tm/lean/test/vendor/omni/Omni.lean +2 -2
- package/project/.sdk/tm/lua/test/vendor/omni/json.lua +2 -2
- package/project/.sdk/tm/lua/test/vendor/omni/regex.lua +2 -2
- package/project/.sdk/tm/lua/test/vendor/omni/runner.lua +2 -2
- package/project/.sdk/tm/lua/test/vendor/omni/util.lua +2 -2
- package/project/.sdk/tm/lua/utility/struct/regex.lua +1 -1
- package/project/.sdk/tm/lua/utility/struct/struct.lua +1 -1
- package/project/.sdk/tm/ocaml/test/vendor/omni/omni.ml +2 -2
- package/project/.sdk/tm/perl/lib/Voxgig/Struct.pm +1 -1
- package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni/Runner.pm +1 -1
- package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni/Util.pm +1 -1
- package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni.pm +1 -1
- package/project/.sdk/tm/php/test/vendor/omni/Runner.php +2 -2
- package/project/.sdk/tm/php/test/vendor/omni/Util.php +2 -2
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/__init__.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/capability.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/catalog.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/config.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/depend.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/env.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/export.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/graph.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/host.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/order.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/point.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/ref.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/resolve.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/types.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/version.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/__init__.py +3 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/addr.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/aws.py +3 -3
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/azuresecrets.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/boru.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/doppler.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/gcpsecrets.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/hashicorp.py +3 -4
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/httpjson.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/infisical.py +4 -6
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/onepassword.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/secretspec.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/sigv4.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/providers.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/sekreto.py +21 -1
- package/project/.sdk/tm/py/pkg/utility/voxgig_struct/voxgig_struct.py +1 -1
- package/project/.sdk/tm/py/test/voxgig_omni/__init__.py +1 -1
- package/project/.sdk/tm/py/test/voxgig_omni/runner.py +16 -2
- package/project/.sdk/tm/py/test/voxgig_omni/util.py +27 -12
- package/project/.sdk/tm/rb/test/vendor/omni/runner.rb +2 -2
- package/project/.sdk/tm/rb/test/vendor/omni/util.rb +2 -2
- package/project/.sdk/tm/rb/test/vendor/omni/voxgig_omni.rb +2 -2
- package/project/.sdk/tm/rust/tests/vendor/omni/json.rs +2 -2
- package/project/.sdk/tm/rust/tests/vendor/omni/mod.rs +2 -2
- package/project/.sdk/tm/rust/tests/vendor/omni/regex.rs +2 -2
- package/project/.sdk/tm/rust/tests/vendor/omni/runner.rs +2 -2
- package/project/.sdk/tm/rust/tests/vendor/omni/util.rs +2 -2
- package/project/.sdk/tm/scala/sdktest/vendor/omni/Json.scala +2 -2
- package/project/.sdk/tm/scala/sdktest/vendor/omni/Runner.scala +2 -2
- package/project/.sdk/tm/scala/sdktest/vendor/omni/Util.scala +2 -2
- package/project/.sdk/tm/swift/Tests/vendor/omni/Json.swift +2 -2
- package/project/.sdk/tm/swift/Tests/vendor/omni/Runner.swift +2 -2
- package/project/.sdk/tm/swift/Tests/vendor/omni/Util.swift +2 -2
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Capability.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Catalog.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Config.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Depend.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Env.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Export.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/FeatureHost.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Graph.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Host.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Order.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Point.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Ref.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Resolve.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Types.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Version.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/index.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/aws.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/azuresecrets.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/boru.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/doppler.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/gcpsecrets.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/hashicorp.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/httpjson.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/infisical.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/onepassword.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/secretspec.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/sigv4.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/builtin.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +1 -1
- package/project/.sdk/tm/ts/src/utility/StructUtility.ts +2 -2
- package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +1 -1
- package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +1 -1
- package/project/.sdk/tm/ts/test/vendor/omni/index.ts +1 -1
- package/project/.sdk/tm/zig/Makefile +15 -1
- package/project/.sdk/tm/zig/build.zig +23 -5
- package/project/.sdk/tm/zig/core/helpers.zig +52 -11
- package/project/.sdk/tm/zig/core/mem.zig +17 -1
- package/project/.sdk/tm/zig/core/utility.zig +23 -19
- package/project/.sdk/tm/zig/feature/audit.zig +2 -2
- package/project/.sdk/tm/zig/feature/cache.zig +2 -2
- package/project/.sdk/tm/zig/feature/debug.zig +5 -5
- package/project/.sdk/tm/zig/feature/netsim.zig +2 -2
- package/project/.sdk/tm/zig/feature/paging.zig +2 -2
- package/project/.sdk/tm/zig/feature/proxy.zig +15 -8
- package/project/.sdk/tm/zig/feature/streaming.zig +5 -5
- package/project/.sdk/tm/zig/feature/support.zig +3 -3
- package/project/.sdk/tm/zig/feature/telemetry.zig +5 -5
- package/project/.sdk/tm/zig/feature/test.zig +10 -5
- package/project/.sdk/tm/zig/test/fh.zig +10 -10
- package/project/.sdk/tm/zig/test/gotcha_test.zig +5 -5
- package/project/.sdk/tm/zig/test/omniresolver.zig +515 -0
- package/project/.sdk/tm/zig/test/omnismoke_test.zig +284 -0
- package/project/.sdk/tm/zig/test/pipeline_test.zig +8 -8
- package/project/.sdk/tm/zig/test/primary_utility_test.zig +144 -154
- package/project/.sdk/tm/zig/test/struct_corpus.zig +264 -187
- package/project/.sdk/tm/zig/test/vendor/omni/omni.zig +1314 -0
- package/project/.sdk/tm/zig/test/vendor/omni/regex.zig +446 -0
- package/project/.sdk/tm/zig/utility/voxgigstruct/regex.zig +7 -4
- package/project/.sdk/tm/zig/utility/voxgigstruct/struct.zig +356 -157
- package/project/sdkgen-package.json +1 -1
- package/project/.sdk/tm/zig/test/struct_runner.zig +0 -465
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// VENDORED: @voxgig/struct sdk-20260907-0029-0 (zig/src/struct.zig)
|
|
2
|
+
// Source: https://github.com/voxgig/struct @ adda9521c828147573df5a8878408a3f2193ddf4 [tag: sdk-20260907-0029-0]
|
|
3
|
+
// License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
1
4
|
// Copyright (c) 2025-2026 Voxgig Ltd. MIT LICENSE.
|
|
2
5
|
|
|
3
6
|
// Voxgig Struct
|
|
@@ -17,18 +20,24 @@ pub const StdJsonValue = std.json.Value;
|
|
|
17
20
|
// All holders of the same *MapRef / *ListRef see mutations.
|
|
18
21
|
// ============================================================================
|
|
19
22
|
|
|
20
|
-
pub const MapData = std.
|
|
21
|
-
pub const ListData = std.
|
|
23
|
+
pub const MapData = std.array_hash_map.String(JsonValue);
|
|
24
|
+
pub const ListData = std.array_list.Managed(JsonValue);
|
|
22
25
|
|
|
23
26
|
pub const MapRef = struct {
|
|
24
27
|
data: MapData,
|
|
28
|
+
// 0.16 has no managed array hash map - only the unmanaged
|
|
29
|
+
// std.array_hash_map.String - so the allocator that used to live inside
|
|
30
|
+
// MapData lives here instead. Holding it on the ref rather than
|
|
31
|
+
// threading it through every caller keeps this type's API, and so every
|
|
32
|
+
// call site of it, exactly as it was.
|
|
33
|
+
allocator: Allocator,
|
|
25
34
|
|
|
26
35
|
pub fn get(self: *const MapRef, key: []const u8) ?JsonValue {
|
|
27
36
|
return self.data.get(key);
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
pub fn put(self: *MapRef, key: []const u8, val: JsonValue) !void {
|
|
31
|
-
try self.data.put(key, val);
|
|
40
|
+
try self.data.put(self.allocator, key, val);
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
pub fn count(self: *const MapRef) usize {
|
|
@@ -61,14 +70,7 @@ pub const ListRef = struct {
|
|
|
61
70
|
};
|
|
62
71
|
|
|
63
72
|
// Function signature for values stored in the JSON tree.
|
|
64
|
-
|
|
65
|
-
// custom utilities, injected clocks/keygens and mock transports can hold
|
|
66
|
-
// state. Stored by pointer so it survives `clone` (shallow) unchanged.
|
|
67
|
-
pub const Callable = struct {
|
|
68
|
-
ctx: *anyopaque,
|
|
69
|
-
call: *const fn (ctx: *anyopaque, allocator: Allocator, arg: JsonValue) anyerror!JsonValue,
|
|
70
|
-
};
|
|
71
|
-
pub const JsonFunc = *const Callable;
|
|
73
|
+
pub const JsonFunc = *const fn (allocator: Allocator) anyerror!JsonValue;
|
|
72
74
|
|
|
73
75
|
// Custom value type with pointer-stable containers.
|
|
74
76
|
pub const JsonValue = union(enum) {
|
|
@@ -84,7 +86,7 @@ pub const JsonValue = union(enum) {
|
|
|
84
86
|
|
|
85
87
|
pub fn makeMap(allocator: Allocator) !JsonValue {
|
|
86
88
|
const mr = try allocator.create(MapRef);
|
|
87
|
-
mr.* = .{ .data =
|
|
89
|
+
mr.* = .{ .data = .empty, .allocator = allocator };
|
|
88
90
|
return JsonValue{ .object = mr };
|
|
89
91
|
}
|
|
90
92
|
|
|
@@ -106,10 +108,10 @@ pub fn fromStdJson(allocator: Allocator, jv: StdJsonValue) anyerror!JsonValue {
|
|
|
106
108
|
.number_string => |s| JsonValue{ .number_string = s },
|
|
107
109
|
.object => |obj| {
|
|
108
110
|
const mr = try allocator.create(MapRef);
|
|
109
|
-
mr.* = .{ .data =
|
|
111
|
+
mr.* = .{ .data = .empty, .allocator = allocator };
|
|
110
112
|
var it = obj.iterator();
|
|
111
113
|
while (it.next()) |kv| {
|
|
112
|
-
try mr.data.put(kv.key_ptr.*, try fromStdJson(allocator, kv.value_ptr.*));
|
|
114
|
+
try mr.data.put(allocator, kv.key_ptr.*, try fromStdJson(allocator, kv.value_ptr.*));
|
|
113
115
|
}
|
|
114
116
|
return JsonValue{ .object = mr };
|
|
115
117
|
},
|
|
@@ -134,10 +136,10 @@ pub fn toStdJson(allocator: Allocator, v: JsonValue) anyerror!StdJsonValue {
|
|
|
134
136
|
.string => |s| StdJsonValue{ .string = s },
|
|
135
137
|
.number_string => |s| StdJsonValue{ .number_string = s },
|
|
136
138
|
.object => |mr| {
|
|
137
|
-
var obj
|
|
139
|
+
var obj: std.json.ObjectMap = .empty;
|
|
138
140
|
var it = mr.data.iterator();
|
|
139
141
|
while (it.next()) |kv| {
|
|
140
|
-
try obj.put(kv.key_ptr.*, try toStdJson(allocator, kv.value_ptr.*));
|
|
142
|
+
try obj.put(allocator, kv.key_ptr.*, try toStdJson(allocator, kv.value_ptr.*));
|
|
141
143
|
}
|
|
142
144
|
return StdJsonValue{ .object = obj };
|
|
143
145
|
},
|
|
@@ -227,7 +229,7 @@ pub const T_node: i32 = 1 << 6;
|
|
|
227
229
|
// TYPENAME maps bit position (via leading zeros count) to type name string.
|
|
228
230
|
pub const TYPENAME = [_][]const u8{
|
|
229
231
|
S_any,
|
|
230
|
-
|
|
232
|
+
S_nil,
|
|
231
233
|
S_boolean,
|
|
232
234
|
S_decimal,
|
|
233
235
|
S_integer,
|
|
@@ -407,7 +409,7 @@ pub fn getelem(allocator: Allocator, val: JsonValue, key: JsonValue, alt: JsonVa
|
|
|
407
409
|
|
|
408
410
|
// If alt is a function, call it to get the default value.
|
|
409
411
|
fn resolveAlt(allocator: Allocator, alt: JsonValue) !JsonValue {
|
|
410
|
-
if (alt == .function) return try alt.function
|
|
412
|
+
if (alt == .function) return try alt.function(allocator);
|
|
411
413
|
return alt;
|
|
412
414
|
}
|
|
413
415
|
|
|
@@ -454,7 +456,7 @@ pub fn getprop(allocator: Allocator, val: JsonValue, key: JsonValue, alt: JsonVa
|
|
|
454
456
|
pub fn keysof(allocator: Allocator, val: JsonValue) !JsonValue {
|
|
455
457
|
if (val == .object) {
|
|
456
458
|
const obj = val.object;
|
|
457
|
-
var key_strs = try std.
|
|
459
|
+
var key_strs = try std.array_list.Managed([]const u8).initCapacity(allocator, obj.count());
|
|
458
460
|
defer key_strs.deinit();
|
|
459
461
|
var it = obj.iterator();
|
|
460
462
|
while (it.next()) |kv| {
|
|
@@ -501,7 +503,7 @@ pub fn items(allocator: Allocator, val: JsonValue) !JsonValue {
|
|
|
501
503
|
if (val == .object) {
|
|
502
504
|
const obj = val.object;
|
|
503
505
|
// Get sorted keys
|
|
504
|
-
var key_strs = try std.
|
|
506
|
+
var key_strs = try std.array_list.Managed([]const u8).initCapacity(allocator, obj.count());
|
|
505
507
|
defer key_strs.deinit();
|
|
506
508
|
var it = obj.iterator();
|
|
507
509
|
while (it.next()) |kv| {
|
|
@@ -594,8 +596,8 @@ pub fn clone(allocator: Allocator, val: JsonValue) !JsonValue {
|
|
|
594
596
|
return switch (val) {
|
|
595
597
|
.object => |obj| {
|
|
596
598
|
const new_obj = try allocator.create(MapRef);
|
|
597
|
-
new_obj.* = .{ .data =
|
|
598
|
-
try new_obj.data.ensureTotalCapacity(@intCast(obj.count()));
|
|
599
|
+
new_obj.* = .{ .data = .empty, .allocator = allocator };
|
|
600
|
+
try new_obj.data.ensureTotalCapacity(allocator, @intCast(obj.count()));
|
|
599
601
|
var it = obj.iterator();
|
|
600
602
|
while (it.next()) |kv| {
|
|
601
603
|
const cloned_val = try clone(allocator, kv.value_ptr.*);
|
|
@@ -746,7 +748,7 @@ fn keyStr(buf: *[20]u8, key: JsonValue) []const u8 {
|
|
|
746
748
|
// Escape regex special characters.
|
|
747
749
|
pub fn escre(allocator: Allocator, s: []const u8) ![]const u8 {
|
|
748
750
|
if (s.len == 0) return S_MT;
|
|
749
|
-
var result = std.
|
|
751
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
750
752
|
for (s) |c| {
|
|
751
753
|
if (isReSpecial(c)) {
|
|
752
754
|
try result.append('\\');
|
|
@@ -817,7 +819,7 @@ pub fn re_find(allocator: Allocator, pattern: []const u8, input: []const u8) ?[]
|
|
|
817
819
|
pub fn re_find_all(allocator: Allocator, pattern: []const u8, input: []const u8) ?[][][]const u8 {
|
|
818
820
|
var re = _re_engine.compile(std.heap.page_allocator, pattern) orelse return null;
|
|
819
821
|
defer re.deinit();
|
|
820
|
-
var rows = std.
|
|
822
|
+
var rows = std.array_list.Managed([][]const u8).init(allocator);
|
|
821
823
|
defer rows.deinit();
|
|
822
824
|
var pos: usize = 0;
|
|
823
825
|
while (pos <= input.len) {
|
|
@@ -848,8 +850,8 @@ pub fn re_find_all(allocator: Allocator, pattern: []const u8, input: []const u8)
|
|
|
848
850
|
}
|
|
849
851
|
|
|
850
852
|
/// re_replace — replace every match in `input` with `replacement`. The
|
|
851
|
-
/// replacement string
|
|
852
|
-
///
|
|
853
|
+
/// replacement string supports JS-style refs: $& (whole match), $1..$9
|
|
854
|
+
/// (captures) and $$ (a literal $); anything else is copied verbatim.
|
|
853
855
|
/// On zero-width match the current rune is emitted and we advance by one
|
|
854
856
|
/// byte, mirroring the ECMAScript convention used by other ports.
|
|
855
857
|
pub fn re_replace(allocator: Allocator, pattern: []const u8, input: []const u8, replacement: []const u8) ![]u8 {
|
|
@@ -857,7 +859,7 @@ pub fn re_replace(allocator: Allocator, pattern: []const u8, input: []const u8,
|
|
|
857
859
|
return allocator.dupe(u8, input);
|
|
858
860
|
};
|
|
859
861
|
defer re.deinit();
|
|
860
|
-
var out = std.
|
|
862
|
+
var out = std.array_list.Managed(u8).init(allocator);
|
|
861
863
|
defer out.deinit();
|
|
862
864
|
var pos: usize = 0;
|
|
863
865
|
while (pos <= input.len) {
|
|
@@ -869,7 +871,31 @@ pub fn re_replace(allocator: Allocator, pattern: []const u8, input: []const u8,
|
|
|
869
871
|
const mstart = @as(usize, @intCast(slots[0]));
|
|
870
872
|
const mend = @as(usize, @intCast(slots[1]));
|
|
871
873
|
try out.appendSlice(input[pos..mstart]);
|
|
872
|
-
|
|
874
|
+
var ri: usize = 0;
|
|
875
|
+
while (ri < replacement.len) {
|
|
876
|
+
const ch = replacement[ri];
|
|
877
|
+
if (ch == '$' and ri + 1 < replacement.len) {
|
|
878
|
+
const nc = replacement[ri + 1];
|
|
879
|
+
if (nc == '$') {
|
|
880
|
+
try out.append('$');
|
|
881
|
+
ri += 2;
|
|
882
|
+
continue;
|
|
883
|
+
} else if (nc == '&' or (nc >= '0' and nc <= '9')) {
|
|
884
|
+
const gi: usize = if (nc == '&') 0 else @as(usize, nc - '0');
|
|
885
|
+
if (gi < re.ngroups) {
|
|
886
|
+
const gs = slots[2 * gi];
|
|
887
|
+
const ge = slots[2 * gi + 1];
|
|
888
|
+
if (gs >= 0 and ge >= gs) {
|
|
889
|
+
try out.appendSlice(input[@as(usize, @intCast(gs))..@as(usize, @intCast(ge))]);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
ri += 2;
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
try out.append(ch);
|
|
897
|
+
ri += 1;
|
|
898
|
+
}
|
|
873
899
|
if (mend == mstart) {
|
|
874
900
|
if (mstart < input.len) {
|
|
875
901
|
try out.append(input[mstart]);
|
|
@@ -891,7 +917,7 @@ pub fn re_escape(allocator: Allocator, s: []const u8) ![]const u8 {
|
|
|
891
917
|
// URL-encode a string.
|
|
892
918
|
pub fn escurl(allocator: Allocator, s: []const u8) ![]const u8 {
|
|
893
919
|
if (s.len == 0) return S_MT;
|
|
894
|
-
var result = std.
|
|
920
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
895
921
|
for (s) |c| {
|
|
896
922
|
if (isUrlSafe(c)) {
|
|
897
923
|
try result.append(c);
|
|
@@ -918,8 +944,8 @@ pub fn join(allocator: Allocator, arr: JsonValue, sep: []const u8, urlMode: bool
|
|
|
918
944
|
const sarr: usize = items_list.len;
|
|
919
945
|
|
|
920
946
|
// Filter to non-empty strings
|
|
921
|
-
var filtered = std.
|
|
922
|
-
var indices = std.
|
|
947
|
+
var filtered = std.array_list.Managed([]const u8).init(allocator);
|
|
948
|
+
var indices = std.array_list.Managed(usize).init(allocator);
|
|
923
949
|
for (items_list, 0..) |item, orig_idx| {
|
|
924
950
|
if (item == .string and item.string.len > 0) {
|
|
925
951
|
try filtered.append(item.string);
|
|
@@ -930,7 +956,7 @@ pub fn join(allocator: Allocator, arr: JsonValue, sep: []const u8, urlMode: bool
|
|
|
930
956
|
if (filtered.items.len == 0) return S_MT;
|
|
931
957
|
|
|
932
958
|
// Process separator handling
|
|
933
|
-
var parts = std.
|
|
959
|
+
var parts = std.array_list.Managed([]const u8).init(allocator);
|
|
934
960
|
|
|
935
961
|
for (filtered.items, 0..) |s, fi| {
|
|
936
962
|
var processed = s;
|
|
@@ -967,7 +993,7 @@ pub fn join(allocator: Allocator, arr: JsonValue, sep: []const u8, urlMode: bool
|
|
|
967
993
|
for (parts.items) |p| total_len += p.len;
|
|
968
994
|
total_len += sep.len * (parts.items.len - 1);
|
|
969
995
|
|
|
970
|
-
var result = try std.
|
|
996
|
+
var result = try std.array_list.Managed(u8).initCapacity(allocator, total_len);
|
|
971
997
|
for (parts.items, 0..) |p, i| {
|
|
972
998
|
try result.appendSlice(p);
|
|
973
999
|
if (i < parts.items.len - 1) {
|
|
@@ -993,7 +1019,7 @@ fn trimRight(s: []const u8, c: u8) []const u8 {
|
|
|
993
1019
|
// E.g. "c//d" → "c/d" but "//a" stays "//a".
|
|
994
1020
|
fn collapseInternal(allocator: Allocator, s: []const u8, sep: u8) ![]const u8 {
|
|
995
1021
|
if (s.len < 3) return s;
|
|
996
|
-
var result = std.
|
|
1022
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
997
1023
|
var i: usize = 0;
|
|
998
1024
|
while (i < s.len) {
|
|
999
1025
|
try result.append(s[i]);
|
|
@@ -1021,9 +1047,9 @@ pub fn jsonify(allocator: Allocator, val: JsonValue, indent_size: usize, offset:
|
|
|
1021
1047
|
if (val == .null) return "null";
|
|
1022
1048
|
|
|
1023
1049
|
// Use the standard JSON stringify
|
|
1024
|
-
var result =
|
|
1025
|
-
try jsonifyWrite(val, result.writer
|
|
1026
|
-
return result.
|
|
1050
|
+
var result: std.Io.Writer.Allocating = .init(allocator);
|
|
1051
|
+
try jsonifyWrite(val, &result.writer, indent_size, offset, 0);
|
|
1052
|
+
return result.written();
|
|
1027
1053
|
}
|
|
1028
1054
|
|
|
1029
1055
|
fn jsonifyWrite(val: JsonValue, writer: anytype, indent_size: usize, offset: usize, depth: usize) !void {
|
|
@@ -1110,9 +1136,9 @@ fn writeIndent(writer: anytype, count: usize) !void {
|
|
|
1110
1136
|
|
|
1111
1137
|
// Compact JSON serialization (no whitespace) for partial injection stringification.
|
|
1112
1138
|
pub fn jsonifyCompact(allocator: Allocator, val: JsonValue) ![]const u8 {
|
|
1113
|
-
var result =
|
|
1114
|
-
try jsonifyCompactWrite(val, result.writer
|
|
1115
|
-
return result.
|
|
1139
|
+
var result: std.Io.Writer.Allocating = .init(allocator);
|
|
1140
|
+
try jsonifyCompactWrite(val, &result.writer);
|
|
1141
|
+
return result.written();
|
|
1116
1142
|
}
|
|
1117
1143
|
|
|
1118
1144
|
fn jsonifyCompactWrite(val: JsonValue, writer: anytype) !void {
|
|
@@ -1214,7 +1240,7 @@ fn stringifyColorInner(allocator: Allocator, val: JsonValue, depth: usize) ![]co
|
|
|
1214
1240
|
break :blk try std.fmt.allocPrint(allocator, "{d}", .{f});
|
|
1215
1241
|
},
|
|
1216
1242
|
.array => |arr| blk: {
|
|
1217
|
-
var result = std.
|
|
1243
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
1218
1244
|
try result.appendSlice(open_color);
|
|
1219
1245
|
try result.append('[');
|
|
1220
1246
|
try result.appendSlice(reset);
|
|
@@ -1229,12 +1255,12 @@ fn stringifyColorInner(allocator: Allocator, val: JsonValue, depth: usize) ![]co
|
|
|
1229
1255
|
break :blk result.items;
|
|
1230
1256
|
},
|
|
1231
1257
|
.object => |obj| blk: {
|
|
1232
|
-
var key_list = std.
|
|
1258
|
+
var key_list = std.array_list.Managed([]const u8).init(allocator);
|
|
1233
1259
|
var it = obj.iterator();
|
|
1234
1260
|
while (it.next()) |kv| try key_list.append(kv.key_ptr.*);
|
|
1235
1261
|
std.mem.sort([]const u8, key_list.items, {}, stringLessThan);
|
|
1236
1262
|
|
|
1237
|
-
var result = std.
|
|
1263
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
1238
1264
|
try result.appendSlice(open_color);
|
|
1239
1265
|
try result.append('{');
|
|
1240
1266
|
try result.appendSlice(reset);
|
|
@@ -1269,7 +1295,7 @@ fn stringifyInner(allocator: Allocator, val: JsonValue) ![]const u8 {
|
|
|
1269
1295
|
return try std.fmt.allocPrint(allocator, "{d}", .{f});
|
|
1270
1296
|
},
|
|
1271
1297
|
.array => |arr| {
|
|
1272
|
-
var result = std.
|
|
1298
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
1273
1299
|
try result.append('[');
|
|
1274
1300
|
for (arr.data.items, 0..) |item, i| {
|
|
1275
1301
|
const s = try stringifyInner(allocator, item);
|
|
@@ -1283,7 +1309,7 @@ fn stringifyInner(allocator: Allocator, val: JsonValue) ![]const u8 {
|
|
|
1283
1309
|
},
|
|
1284
1310
|
.object => |obj| {
|
|
1285
1311
|
// Sort keys
|
|
1286
|
-
var key_list = std.
|
|
1312
|
+
var key_list = std.array_list.Managed([]const u8).init(allocator);
|
|
1287
1313
|
defer key_list.deinit();
|
|
1288
1314
|
var it = obj.iterator();
|
|
1289
1315
|
while (it.next()) |kv| {
|
|
@@ -1291,7 +1317,7 @@ fn stringifyInner(allocator: Allocator, val: JsonValue) ![]const u8 {
|
|
|
1291
1317
|
}
|
|
1292
1318
|
std.mem.sort([]const u8, key_list.items, {}, stringLessThan);
|
|
1293
1319
|
|
|
1294
|
-
var result = std.
|
|
1320
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
1295
1321
|
try result.append('{');
|
|
1296
1322
|
for (key_list.items, 0..) |k, i| {
|
|
1297
1323
|
const v = obj.get(k).?;
|
|
@@ -1313,10 +1339,10 @@ fn stringifyInner(allocator: Allocator, val: JsonValue) ![]const u8 {
|
|
|
1313
1339
|
|
|
1314
1340
|
// Build a human-friendly path string.
|
|
1315
1341
|
pub fn pathify(allocator: Allocator, val: JsonValue, from: usize, end: usize) ![]const u8 {
|
|
1316
|
-
var path: ?std.
|
|
1342
|
+
var path: ?std.array_list.Managed([]const u8) = null;
|
|
1317
1343
|
|
|
1318
1344
|
if (val == .array) {
|
|
1319
|
-
path = std.
|
|
1345
|
+
path = std.array_list.Managed([]const u8).init(allocator);
|
|
1320
1346
|
for (val.array.data.items) |item| {
|
|
1321
1347
|
switch (item) {
|
|
1322
1348
|
.string => |s| try path.?.append(s),
|
|
@@ -1326,10 +1352,10 @@ pub fn pathify(allocator: Allocator, val: JsonValue, from: usize, end: usize) ![
|
|
|
1326
1352
|
}
|
|
1327
1353
|
}
|
|
1328
1354
|
} else if (val == .string) {
|
|
1329
|
-
path = std.
|
|
1355
|
+
path = std.array_list.Managed([]const u8).init(allocator);
|
|
1330
1356
|
try path.?.append(val.string);
|
|
1331
1357
|
} else if (val == .integer or val == .float) {
|
|
1332
|
-
path = std.
|
|
1358
|
+
path = std.array_list.Managed([]const u8).init(allocator);
|
|
1333
1359
|
const num: i64 = if (val == .integer) val.integer else @intFromFloat(@floor(val.float));
|
|
1334
1360
|
try path.?.append(try std.fmt.allocPrint(allocator, "{d}", .{num}));
|
|
1335
1361
|
}
|
|
@@ -1345,9 +1371,9 @@ pub fn pathify(allocator: Allocator, val: JsonValue, from: usize, end: usize) ![
|
|
|
1345
1371
|
}
|
|
1346
1372
|
|
|
1347
1373
|
// Map: replace dots in string parts
|
|
1348
|
-
var mapped = std.
|
|
1374
|
+
var mapped = std.array_list.Managed([]const u8).init(allocator);
|
|
1349
1375
|
for (sliced) |part| {
|
|
1350
|
-
var replaced = std.
|
|
1376
|
+
var replaced = std.array_list.Managed(u8).init(allocator);
|
|
1351
1377
|
for (part) |c| {
|
|
1352
1378
|
if (c != '.') try replaced.append(c);
|
|
1353
1379
|
}
|
|
@@ -1355,7 +1381,7 @@ pub fn pathify(allocator: Allocator, val: JsonValue, from: usize, end: usize) ![
|
|
|
1355
1381
|
}
|
|
1356
1382
|
|
|
1357
1383
|
// Join with dots
|
|
1358
|
-
var result = std.
|
|
1384
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
1359
1385
|
for (mapped.items, 0..) |part, i| {
|
|
1360
1386
|
try result.appendSlice(part);
|
|
1361
1387
|
if (i < mapped.items.len - 1) {
|
|
@@ -1366,7 +1392,7 @@ pub fn pathify(allocator: Allocator, val: JsonValue, from: usize, end: usize) ![
|
|
|
1366
1392
|
}
|
|
1367
1393
|
|
|
1368
1394
|
// Unknown path — always include colon and stringified value
|
|
1369
|
-
var result = std.
|
|
1395
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
1370
1396
|
try result.appendSlice("<unknown-path:");
|
|
1371
1397
|
const s = try stringify(allocator, val, 47);
|
|
1372
1398
|
try result.appendSlice(s);
|
|
@@ -1492,19 +1518,19 @@ pub fn pad(allocator: Allocator, s: []const u8, padding: i64, padchar: u8) ![]co
|
|
|
1492
1518
|
// Walk — depth-first tree traversal with before/after callbacks.
|
|
1493
1519
|
// ============================================================================
|
|
1494
1520
|
|
|
1495
|
-
// Pool of path buffers — one std.
|
|
1521
|
+
// Pool of path buffers — one std.array_list.Managed([]const u8) per depth. Allocated
|
|
1496
1522
|
// once per top-level walk() call, grown on demand, and mutated in place as
|
|
1497
1523
|
// siblings are visited. Avoids allocating a fresh path slice per recursive
|
|
1498
1524
|
// call. Buffers are heap-allocated so that growing the outer list (on
|
|
1499
1525
|
// deeper recursion) does not invalidate pointers held by shallower frames.
|
|
1500
1526
|
const WalkPool = struct {
|
|
1501
1527
|
allocator: Allocator,
|
|
1502
|
-
buffers: std.
|
|
1528
|
+
buffers: std.array_list.Managed(*std.array_list.Managed([]const u8)),
|
|
1503
1529
|
|
|
1504
1530
|
fn init(allocator: Allocator) WalkPool {
|
|
1505
1531
|
return .{
|
|
1506
1532
|
.allocator = allocator,
|
|
1507
|
-
.buffers = std.
|
|
1533
|
+
.buffers = std.array_list.Managed(*std.array_list.Managed([]const u8)).init(allocator),
|
|
1508
1534
|
};
|
|
1509
1535
|
}
|
|
1510
1536
|
|
|
@@ -1517,10 +1543,10 @@ const WalkPool = struct {
|
|
|
1517
1543
|
}
|
|
1518
1544
|
|
|
1519
1545
|
// Return the path buffer for the given depth, growing the pool if needed.
|
|
1520
|
-
fn at(self: *WalkPool, depth: usize) !*std.
|
|
1546
|
+
fn at(self: *WalkPool, depth: usize) !*std.array_list.Managed([]const u8) {
|
|
1521
1547
|
while (self.buffers.items.len <= depth) {
|
|
1522
|
-
const buf = try self.allocator.create(std.
|
|
1523
|
-
buf.* = std.
|
|
1548
|
+
const buf = try self.allocator.create(std.array_list.Managed([]const u8));
|
|
1549
|
+
buf.* = std.array_list.Managed([]const u8).init(self.allocator);
|
|
1524
1550
|
try self.buffers.append(buf);
|
|
1525
1551
|
}
|
|
1526
1552
|
return self.buffers.items[depth];
|
|
@@ -1654,7 +1680,7 @@ pub fn merge(allocator: Allocator, val: JsonValue, maxdepth: i32) !JsonValue {
|
|
|
1654
1680
|
if (islist(last)) return try JsonValue.makeList(allocator);
|
|
1655
1681
|
if (ismap(last)) {
|
|
1656
1682
|
const obj = try allocator.create(MapRef);
|
|
1657
|
-
obj.* = .{ .data =
|
|
1683
|
+
obj.* = .{ .data = .empty, .allocator = allocator };
|
|
1658
1684
|
_ = &obj;
|
|
1659
1685
|
return JsonValue{ .object = obj };
|
|
1660
1686
|
}
|
|
@@ -1820,7 +1846,7 @@ pub fn getpathInj(allocator: Allocator, path_val: JsonValue, store: JsonValue, i
|
|
|
1820
1846
|
if (store == .object) {
|
|
1821
1847
|
if (store.object.get(parts[0])) |v| {
|
|
1822
1848
|
// If the value is a function, call it.
|
|
1823
|
-
if (v == .function) return try v.function
|
|
1849
|
+
if (v == .function) return try v.function(allocator);
|
|
1824
1850
|
return v;
|
|
1825
1851
|
}
|
|
1826
1852
|
}
|
|
@@ -1908,7 +1934,7 @@ pub fn getpathInj(allocator: Allocator, path_val: JsonValue, store: JsonValue, i
|
|
|
1908
1934
|
// Build full path from dpath minus ascends, plus remaining parts.
|
|
1909
1935
|
const dpath = ij.dpath;
|
|
1910
1936
|
const cutLen = if (ascends > dpath.len) 0 else dpath.len - ascends;
|
|
1911
|
-
var fullpath = std.
|
|
1937
|
+
var fullpath = std.array_list.Managed([]const u8).init(allocator);
|
|
1912
1938
|
for (dpath[0..cutLen]) |dp| try fullpath.append(dp);
|
|
1913
1939
|
if (pI + 1 < numparts) {
|
|
1914
1940
|
for (parts[pI + 1 .. numparts]) |rp| try fullpath.append(rp);
|
|
@@ -1966,7 +1992,7 @@ fn resolvePart(allocator: Allocator, val: JsonValue, part_in: []const u8, inj: ?
|
|
|
1966
1992
|
// Handle $$ escape → $.
|
|
1967
1993
|
var part = part_in;
|
|
1968
1994
|
if (std.mem.indexOf(u8, part, "$$")) |_| {
|
|
1969
|
-
var buf = std.
|
|
1995
|
+
var buf = std.array_list.Managed(u8).init(allocator);
|
|
1970
1996
|
var i: usize = 0;
|
|
1971
1997
|
while (i < part.len) {
|
|
1972
1998
|
if (i + 1 < part.len and part[i] == '$' and part[i + 1] == '$') {
|
|
@@ -2121,7 +2147,7 @@ pub const Injection = struct {
|
|
|
2121
2147
|
modify: ?ModifyFn = null,
|
|
2122
2148
|
|
|
2123
2149
|
// Shared error collector (pointer so all children share it).
|
|
2124
|
-
errs: *std.
|
|
2150
|
+
errs: *std.array_list.Managed([]const u8),
|
|
2125
2151
|
|
|
2126
2152
|
// Create a child injection for processing key at keys[key_i].
|
|
2127
2153
|
pub fn child(self: *Injection, key_i: usize, keys: []const []const u8) !*Injection {
|
|
@@ -2248,16 +2274,16 @@ pub fn inject(allocator: Allocator, val: JsonValue, store: JsonValue, inj_opt: ?
|
|
|
2248
2274
|
if (inj_opt == null or (inj_opt != null and inj_opt.?.mode == 0)) {
|
|
2249
2275
|
// Root injection: wrap val in a virtual parent.
|
|
2250
2276
|
const parent_obj = try allocator.create(MapRef);
|
|
2251
|
-
parent_obj.* = .{ .data =
|
|
2277
|
+
parent_obj.* = .{ .data = .empty, .allocator = allocator };
|
|
2252
2278
|
try parent_obj.put(S_DTOP, val);
|
|
2253
2279
|
const parent_val = JsonValue{ .object = parent_obj };
|
|
2254
2280
|
|
|
2255
|
-
var errs: *std.
|
|
2281
|
+
var errs: *std.array_list.Managed([]const u8) = undefined;
|
|
2256
2282
|
if (inj_opt) |existing| {
|
|
2257
2283
|
errs = existing.errs;
|
|
2258
2284
|
} else {
|
|
2259
|
-
errs = try allocator.create(std.
|
|
2260
|
-
errs.* = std.
|
|
2285
|
+
errs = try allocator.create(std.array_list.Managed([]const u8));
|
|
2286
|
+
errs.* = std.array_list.Managed([]const u8).init(allocator);
|
|
2261
2287
|
}
|
|
2262
2288
|
|
|
2263
2289
|
var init_keys = try allocator.alloc([]const u8, 1);
|
|
@@ -2303,8 +2329,8 @@ pub fn inject(allocator: Allocator, val: JsonValue, store: JsonValue, inj_opt: ?
|
|
|
2303
2329
|
|
|
2304
2330
|
if (isnode(val)) {
|
|
2305
2331
|
// Get sorted keys: normal first, then $ transform keys.
|
|
2306
|
-
var normal_keys = std.
|
|
2307
|
-
var transform_keys = std.
|
|
2332
|
+
var normal_keys = std.array_list.Managed([]const u8).init(allocator);
|
|
2333
|
+
var transform_keys = std.array_list.Managed([]const u8).init(allocator);
|
|
2308
2334
|
|
|
2309
2335
|
const all_keys = try keysof(allocator, current);
|
|
2310
2336
|
if (all_keys == .array) {
|
|
@@ -2319,7 +2345,7 @@ pub fn inject(allocator: Allocator, val: JsonValue, store: JsonValue, inj_opt: ?
|
|
|
2319
2345
|
}
|
|
2320
2346
|
}
|
|
2321
2347
|
|
|
2322
|
-
var node_keys = std.
|
|
2348
|
+
var node_keys = std.array_list.Managed([]const u8).init(allocator);
|
|
2323
2349
|
for (normal_keys.items) |k| try node_keys.append(k);
|
|
2324
2350
|
for (transform_keys.items) |k| try node_keys.append(k);
|
|
2325
2351
|
|
|
@@ -2340,7 +2366,7 @@ pub fn inject(allocator: Allocator, val: JsonValue, store: JsonValue, inj_opt: ?
|
|
|
2340
2366
|
// Injection may modify child processing state.
|
|
2341
2367
|
nkI = @as(isize, @bitCast(childinj.key_i));
|
|
2342
2368
|
node_keys = blk: {
|
|
2343
|
-
var nk = std.
|
|
2369
|
+
var nk = std.array_list.Managed([]const u8).init(allocator);
|
|
2344
2370
|
for (childinj.keys) |k| try nk.append(k);
|
|
2345
2371
|
break :blk nk;
|
|
2346
2372
|
};
|
|
@@ -2357,7 +2383,7 @@ pub fn inject(allocator: Allocator, val: JsonValue, store: JsonValue, inj_opt: ?
|
|
|
2357
2383
|
|
|
2358
2384
|
nkI = @as(isize, @bitCast(childinj.key_i));
|
|
2359
2385
|
node_keys = blk: {
|
|
2360
|
-
var nk = std.
|
|
2386
|
+
var nk = std.array_list.Managed([]const u8).init(allocator);
|
|
2361
2387
|
for (childinj.keys) |k| try nk.append(k);
|
|
2362
2388
|
break :blk nk;
|
|
2363
2389
|
};
|
|
@@ -2369,7 +2395,7 @@ pub fn inject(allocator: Allocator, val: JsonValue, store: JsonValue, inj_opt: ?
|
|
|
2369
2395
|
|
|
2370
2396
|
nkI = @as(isize, @bitCast(childinj.key_i));
|
|
2371
2397
|
node_keys = blk: {
|
|
2372
|
-
var nk = std.
|
|
2398
|
+
var nk = std.array_list.Managed([]const u8).init(allocator);
|
|
2373
2399
|
for (childinj.keys) |k| try nk.append(k);
|
|
2374
2400
|
break :blk nk;
|
|
2375
2401
|
};
|
|
@@ -2385,19 +2411,21 @@ pub fn inject(allocator: Allocator, val: JsonValue, store: JsonValue, inj_opt: ?
|
|
|
2385
2411
|
if (!inj.skip and (result != .null or val != .null)) {
|
|
2386
2412
|
_ = try inj.setval(result, 0);
|
|
2387
2413
|
}
|
|
2388
|
-
inj.skip = false;
|
|
2389
2414
|
current = result;
|
|
2390
2415
|
}
|
|
2391
2416
|
|
|
2392
2417
|
inj.val = current;
|
|
2393
2418
|
|
|
2394
|
-
// Call modify callback if set (skip suppresses modify too
|
|
2419
|
+
// Call modify callback if set (skip suppresses modify too — the zig
|
|
2420
|
+
// spelling of the canonical `SKIP !== val` guard around both setval
|
|
2421
|
+
// and modify). Reset skip only after both checks have seen it.
|
|
2395
2422
|
if (!inj.skip) {
|
|
2396
2423
|
if (inj.modify) |modify_fn| {
|
|
2397
2424
|
const mval = getprop(allocator, inj.parent, JsonValue{ .string = inj.key }, .null) catch .null;
|
|
2398
2425
|
modify_fn(allocator, mval, inj.key, inj.parent, inj, store);
|
|
2399
2426
|
}
|
|
2400
2427
|
}
|
|
2428
|
+
inj.skip = false;
|
|
2401
2429
|
|
|
2402
2430
|
// Return value is the top-level result.
|
|
2403
2431
|
return try getprop(allocator, inj.parent, JsonValue{ .string = S_DTOP }, .null);
|
|
@@ -2432,7 +2460,7 @@ fn injectStr(allocator: Allocator, val: []const u8, store: JsonValue, inj: *Inje
|
|
|
2432
2460
|
|
|
2433
2461
|
// Partial injection: replace each `ref` segment.
|
|
2434
2462
|
inj.full = false;
|
|
2435
|
-
var result = std.
|
|
2463
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
2436
2464
|
var i: usize = 0;
|
|
2437
2465
|
while (i < val.len) {
|
|
2438
2466
|
if (val[i] == '`') {
|
|
@@ -2492,7 +2520,27 @@ fn resolvePathOrCmd(allocator: Allocator, pathref: []const u8, store: JsonValue,
|
|
|
2492
2520
|
return try getpathInj(allocator, JsonValue{ .string = pathref }, store, inj);
|
|
2493
2521
|
}
|
|
2494
2522
|
|
|
2495
|
-
//
|
|
2523
|
+
// Validation metadata references carry an operator: `$~` injects the
|
|
2524
|
+
// resolved value, while `$=` rewrites the slot as an $EXACT validator
|
|
2525
|
+
// and revisits it. Other absolute paths retain ordinary getpath
|
|
2526
|
+
// semantics (notably the empty path resolves to $TOP, not the store).
|
|
2527
|
+
if (inj.validate_mode and
|
|
2528
|
+
(std.mem.indexOf(u8, pathref, "$~") != null or std.mem.indexOf(u8, pathref, "$=") != null))
|
|
2529
|
+
{
|
|
2530
|
+
const found = try getpathInj(allocator, JsonValue{ .string = pathref }, store, inj);
|
|
2531
|
+
if (std.mem.indexOf(u8, pathref, "$=") != null) {
|
|
2532
|
+
const exact = try allocator.create(ListRef);
|
|
2533
|
+
exact.* = .{ .data = ListData.init(allocator) };
|
|
2534
|
+
try exact.append(JsonValue{ .string = S_BEXACT });
|
|
2535
|
+
try exact.append(found);
|
|
2536
|
+
_ = try inj.setval(JsonValue{ .array = exact }, 0);
|
|
2537
|
+
inj.key_i = std.math.maxInt(usize);
|
|
2538
|
+
inj.skip = true;
|
|
2539
|
+
return .null;
|
|
2540
|
+
}
|
|
2541
|
+
return found;
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2496
2544
|
return try getpath(allocator, JsonValue{ .string = pathref }, store);
|
|
2497
2545
|
}
|
|
2498
2546
|
|
|
@@ -2548,6 +2596,7 @@ fn dispatchCmd(allocator: Allocator, cmd: []const u8, store: JsonValue, inj: *In
|
|
|
2548
2596
|
if (std.mem.eql(u8, cmd, "$DECIMAL")) return try cmdValidateType(allocator, inj, S_decimal, T_decimal);
|
|
2549
2597
|
if (std.mem.eql(u8, cmd, "$BOOLEAN")) return try cmdValidateType(allocator, inj, S_boolean, T_boolean);
|
|
2550
2598
|
if (std.mem.eql(u8, cmd, "$NULL")) return try cmdValidateType(allocator, inj, S_null, T_null);
|
|
2599
|
+
if (std.mem.eql(u8, cmd, "$NIL")) return try cmdValidateNil(allocator, inj);
|
|
2551
2600
|
if (std.mem.eql(u8, cmd, "$OBJECT") or std.mem.eql(u8, cmd, "$MAP"))
|
|
2552
2601
|
return try cmdValidateType(allocator, inj, S_map, T_map);
|
|
2553
2602
|
if (std.mem.eql(u8, cmd, "$ARRAY") or std.mem.eql(u8, cmd, "$LIST"))
|
|
@@ -2562,7 +2611,7 @@ fn dispatchCmd(allocator: Allocator, cmd: []const u8, store: JsonValue, inj: *In
|
|
|
2562
2611
|
if (store == .object) {
|
|
2563
2612
|
if (store.object.get(cmd)) |val| {
|
|
2564
2613
|
if (val == .function) {
|
|
2565
|
-
return try val.function
|
|
2614
|
+
return try val.function(allocator);
|
|
2566
2615
|
}
|
|
2567
2616
|
return val;
|
|
2568
2617
|
}
|
|
@@ -2701,12 +2750,23 @@ fn cmdValidateAny(inj: *Injection) JsonValue {
|
|
|
2701
2750
|
return out;
|
|
2702
2751
|
}
|
|
2703
2752
|
|
|
2753
|
+
fn cmdValidateNil(allocator: Allocator, inj: *Injection) anyerror!JsonValue {
|
|
2754
|
+
if (inj.mode != M_VAL) return .null;
|
|
2755
|
+
const out = getprop(allocator, inj.dparent, JsonValue{ .string = inj.key }, .null) catch .null;
|
|
2756
|
+
if (out != .null) {
|
|
2757
|
+
try inj.errs.append(try invalidTypeMsg(allocator, inj.path, S_nil, out));
|
|
2758
|
+
}
|
|
2759
|
+
_ = try inj.setval(out, 0);
|
|
2760
|
+
return out;
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2704
2763
|
fn cmdValidateOne(allocator: Allocator, inj: *Injection, _: JsonValue) anyerror!JsonValue {
|
|
2705
2764
|
if (inj.mode != M_VAL) return .null;
|
|
2706
2765
|
if (inj.parent != .array) return .null;
|
|
2707
2766
|
|
|
2708
2767
|
// Skip remaining keys in the list.
|
|
2709
2768
|
inj.key_i = inj.keys.len;
|
|
2769
|
+
inj.skip = true;
|
|
2710
2770
|
|
|
2711
2771
|
const parent_items = inj.parent.array.data.items;
|
|
2712
2772
|
if (parent_items.len < 2) return .null;
|
|
@@ -2721,29 +2781,61 @@ fn cmdValidateOne(allocator: Allocator, inj: *Injection, _: JsonValue) anyerror!
|
|
|
2721
2781
|
// Try each alternative.
|
|
2722
2782
|
const alts = parent_items[1..];
|
|
2723
2783
|
for (alts) |alt| {
|
|
2724
|
-
const terrs = std.
|
|
2725
|
-
const terrs_ptr = try allocator.create(std.
|
|
2784
|
+
const terrs = std.array_list.Managed([]const u8).init(allocator);
|
|
2785
|
+
const terrs_ptr = try allocator.create(std.array_list.Managed([]const u8));
|
|
2726
2786
|
terrs_ptr.* = terrs;
|
|
2727
2787
|
_ = try validateWalk(allocator, alt, data_val, terrs_ptr, if (inj.path.len > 1) inj.path[1..] else inj.path);
|
|
2728
2788
|
if (terrs_ptr.items.len == 0) return .null;
|
|
2729
2789
|
}
|
|
2730
2790
|
|
|
2731
2791
|
// No match — build error message.
|
|
2732
|
-
var desc = std.
|
|
2792
|
+
var desc = std.array_list.Managed(u8).init(allocator);
|
|
2733
2793
|
for (alts, 0..) |alt, i| {
|
|
2734
2794
|
if (i > 0) try desc.appendSlice(", ");
|
|
2735
2795
|
try desc.appendSlice(try stringify(allocator, alt, null));
|
|
2736
2796
|
}
|
|
2737
2797
|
const prefix = if (alts.len > 1) "one of " else "";
|
|
2738
|
-
|
|
2798
|
+
// Canonical does `inj.path = slice(inj.path, -1)`: $ONE wraps the value
|
|
2799
|
+
// in its parent, so the error belongs to the parent's path, not to the
|
|
2800
|
+
// list slot the command happens to occupy. Without this every message
|
|
2801
|
+
// carried a trailing `.0`.
|
|
2802
|
+
const reportpath = if (0 < inj.path.len) inj.path[0 .. inj.path.len - 1] else inj.path;
|
|
2803
|
+
const named = try transformNames(allocator, desc.items);
|
|
2804
|
+
try inj.errs.append(try invalidTypeMsg(allocator, reportpath, try std.fmt.allocPrint(allocator, "{s}{s}", .{ prefix, named }), data_val));
|
|
2739
2805
|
return .null;
|
|
2740
2806
|
}
|
|
2741
2807
|
|
|
2808
|
+
/// Canonical renders a validator description through R_TRANSFORM_NAME
|
|
2809
|
+
/// (/`\$([A-Z]+)`/g), lowercasing the captured name: the alternatives of
|
|
2810
|
+
/// `["`$ONE`","`$STRING`","`$NUMBER`"]` read back as "string, number", not
|
|
2811
|
+
/// as the spec literals. Applied to the joined description, so every
|
|
2812
|
+
/// command name inside it is rewritten.
|
|
2813
|
+
fn transformNames(allocator: Allocator, src: []const u8) ![]const u8 {
|
|
2814
|
+
var out = std.array_list.Managed(u8).init(allocator);
|
|
2815
|
+
var i: usize = 0;
|
|
2816
|
+
while (i < src.len) {
|
|
2817
|
+
// `$NAME` - backtick, dollar, one or more A-Z, backtick.
|
|
2818
|
+
if (src[i] == '`' and i + 2 < src.len and src[i + 1] == '$') {
|
|
2819
|
+
var j = i + 2;
|
|
2820
|
+
while (j < src.len and src[j] >= 'A' and src[j] <= 'Z') : (j += 1) {}
|
|
2821
|
+
if (j > i + 2 and j < src.len and src[j] == '`') {
|
|
2822
|
+
for (src[i + 2 .. j]) |c| try out.append(c - 'A' + 'a');
|
|
2823
|
+
i = j + 1;
|
|
2824
|
+
continue;
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
try out.append(src[i]);
|
|
2828
|
+
i += 1;
|
|
2829
|
+
}
|
|
2830
|
+
return out.items;
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2742
2833
|
fn cmdValidateExactCmd(allocator: Allocator, inj: *Injection) anyerror!JsonValue {
|
|
2743
2834
|
if (inj.mode != M_VAL) return .null;
|
|
2744
2835
|
if (inj.parent != .array) return .null;
|
|
2745
2836
|
|
|
2746
2837
|
inj.key_i = inj.keys.len;
|
|
2838
|
+
inj.skip = true;
|
|
2747
2839
|
|
|
2748
2840
|
const parent_items = inj.parent.array.data.items;
|
|
2749
2841
|
if (parent_items.len < 2) return .null;
|
|
@@ -2764,13 +2856,16 @@ fn cmdValidateExactCmd(allocator: Allocator, inj: *Injection) anyerror!JsonValue
|
|
|
2764
2856
|
if (std.mem.eql(u8, sa, sb)) return .null;
|
|
2765
2857
|
}
|
|
2766
2858
|
|
|
2767
|
-
var desc = std.
|
|
2859
|
+
var desc = std.array_list.Managed(u8).init(allocator);
|
|
2768
2860
|
for (alts, 0..) |alt, i| {
|
|
2769
2861
|
if (i > 0) try desc.appendSlice(", ");
|
|
2770
2862
|
try desc.appendSlice(try stringify(allocator, alt, null));
|
|
2771
2863
|
}
|
|
2772
2864
|
const prefix = if (alts.len > 1) "one of " else "";
|
|
2773
|
-
|
|
2865
|
+
// Same parent-path rule as $ONE above.
|
|
2866
|
+
const reportpath = if (0 < inj.path.len) inj.path[0 .. inj.path.len - 1] else inj.path;
|
|
2867
|
+
const root_prefix = if (1 < reportpath.len) "" else "value ";
|
|
2868
|
+
try inj.errs.append(try invalidTypeMsg(allocator, reportpath, try std.fmt.allocPrint(allocator, "{s}exactly equal to {s}{s}", .{ root_prefix, prefix, desc.items }), data_val));
|
|
2774
2869
|
return .null;
|
|
2775
2870
|
}
|
|
2776
2871
|
|
|
@@ -2825,8 +2920,27 @@ fn cmdValidateChildCmd(allocator: Allocator, inj: *Injection, store: JsonValue)
|
|
|
2825
2920
|
while (li < dlen) : (li += 1) {
|
|
2826
2921
|
try inj.parent.array.append(try clone(allocator, child));
|
|
2827
2922
|
}
|
|
2828
|
-
|
|
2829
|
-
|
|
2923
|
+
|
|
2924
|
+
// NOTE: modifying inj! This extends the child value loop in inject
|
|
2925
|
+
// to cover every cloned child.
|
|
2926
|
+
var ckey_i: usize = inj.keys.len;
|
|
2927
|
+
while (ckey_i < inj.parent.array.data.items.len) : (ckey_i += 1) {
|
|
2928
|
+
const ckey = try strkey(allocator, JsonValue{ .integer = @intCast(ckey_i) });
|
|
2929
|
+
inj.keys = appendSlice(allocator, []const u8, inj.keys, ckey) catch inj.keys;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
// Restart the child value loop at the first element (the loop
|
|
2933
|
+
// increments its signed nkI counter on resume) so that the first
|
|
2934
|
+
// element is also validated against the child template. key_i is
|
|
2935
|
+
// usize: max-usize bit-casts to -1 in inject's nkI — the same wrap
|
|
2936
|
+
// idiom cmdRef already relies on.
|
|
2937
|
+
inj.key_i = std.math.maxInt(usize);
|
|
2938
|
+
|
|
2939
|
+
// Suppress setval (and modify) so the cloned child template stays
|
|
2940
|
+
// in place at the first element for the resumed loop to validate.
|
|
2941
|
+
// This is the zig spelling of the canonical `return SKIP`.
|
|
2942
|
+
inj.skip = true;
|
|
2943
|
+
return .null;
|
|
2830
2944
|
}
|
|
2831
2945
|
|
|
2832
2946
|
return .null;
|
|
@@ -2870,7 +2984,7 @@ fn cmdFormat(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue
|
|
|
2870
2984
|
return out;
|
|
2871
2985
|
}
|
|
2872
2986
|
|
|
2873
|
-
fn applyFormat(allocator: Allocator, name: []const u8, val: JsonValue, errs: *std.
|
|
2987
|
+
fn applyFormat(allocator: Allocator, name: []const u8, val: JsonValue, errs: *std.array_list.Managed([]const u8)) !JsonValue {
|
|
2874
2988
|
if (std.mem.eql(u8, name, "upper")) return try walkFormat(allocator, val, fmtUpper);
|
|
2875
2989
|
if (std.mem.eql(u8, name, "lower")) return try walkFormat(allocator, val, fmtLower);
|
|
2876
2990
|
if (std.mem.eql(u8, name, "string")) return try walkFormat(allocator, val, fmtString);
|
|
@@ -2879,7 +2993,7 @@ fn applyFormat(allocator: Allocator, name: []const u8, val: JsonValue, errs: *st
|
|
|
2879
2993
|
if (std.mem.eql(u8, name, "identity")) return val;
|
|
2880
2994
|
if (std.mem.eql(u8, name, "concat")) {
|
|
2881
2995
|
if (val == .array) {
|
|
2882
|
-
var buf = std.
|
|
2996
|
+
var buf = std.array_list.Managed(u8).init(allocator);
|
|
2883
2997
|
for (val.array.data.items) |item| {
|
|
2884
2998
|
if (isnode(item)) continue;
|
|
2885
2999
|
try buf.appendSlice(try fmtStr(allocator, item));
|
|
@@ -2898,7 +3012,7 @@ const FormatFn = *const fn (Allocator, JsonValue) anyerror!JsonValue;
|
|
|
2898
3012
|
fn walkFormat(allocator: Allocator, val: JsonValue, fmt_fn: FormatFn) !JsonValue {
|
|
2899
3013
|
if (val == .object) {
|
|
2900
3014
|
const new_obj = try allocator.create(MapRef);
|
|
2901
|
-
new_obj.* = .{ .data =
|
|
3015
|
+
new_obj.* = .{ .data = .empty, .allocator = allocator };
|
|
2902
3016
|
var it = val.object.iterator();
|
|
2903
3017
|
while (it.next()) |kv| {
|
|
2904
3018
|
try new_obj.put(kv.key_ptr.*, try walkFormat(allocator, kv.value_ptr.*, fmt_fn));
|
|
@@ -3009,9 +3123,9 @@ fn cmdEach(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3009
3123
|
JsonValue{ .null = {} };
|
|
3010
3124
|
|
|
3011
3125
|
// Build source value list and key list.
|
|
3012
|
-
var src_vals = std.
|
|
3013
|
-
var src_keys = std.
|
|
3014
|
-
var tval_items = std.
|
|
3126
|
+
var src_vals = std.array_list.Managed(JsonValue).init(allocator);
|
|
3127
|
+
var src_keys = std.array_list.Managed([]const u8).init(allocator);
|
|
3128
|
+
var tval_items = std.array_list.Managed(JsonValue).init(allocator);
|
|
3015
3129
|
|
|
3016
3130
|
if (islist(src)) {
|
|
3017
3131
|
for (src.array.data.items, 0..) |src_item, idx| {
|
|
@@ -3031,7 +3145,7 @@ fn cmdEach(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3031
3145
|
// Add $ANNO with $KEY for map sources.
|
|
3032
3146
|
if (cclone == .object) {
|
|
3033
3147
|
const anno = try allocator.create(MapRef);
|
|
3034
|
-
anno.* = .{ .data =
|
|
3148
|
+
anno.* = .{ .data = .empty, .allocator = allocator };
|
|
3035
3149
|
try anno.put(S_KEY, pair.array.data.items[0]);
|
|
3036
3150
|
_ = try setprop(allocator, cclone, JsonValue{ .string = S_BANNO }, JsonValue{ .object = anno });
|
|
3037
3151
|
}
|
|
@@ -3050,7 +3164,7 @@ fn cmdEach(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3050
3164
|
// Build per-item store: original store + $TOP = src_item.
|
|
3051
3165
|
// Also merge root data keys for ancestor path access.
|
|
3052
3166
|
const each_store = try allocator.create(MapRef);
|
|
3053
|
-
each_store.* = .{ .data =
|
|
3167
|
+
each_store.* = .{ .data = .empty, .allocator = allocator };
|
|
3054
3168
|
if (store == .object) {
|
|
3055
3169
|
var sit = store.object.iterator();
|
|
3056
3170
|
while (sit.next()) |kv| try each_store.put(kv.key_ptr.*, kv.value_ptr.*);
|
|
@@ -3100,8 +3214,8 @@ fn cmdPack(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3100
3214
|
try getpath(allocator, JsonValue{ .string = srcpath }, store);
|
|
3101
3215
|
|
|
3102
3216
|
// Normalize source to list.
|
|
3103
|
-
var src_list = std.
|
|
3104
|
-
var src_keys = std.
|
|
3217
|
+
var src_list = std.array_list.Managed(JsonValue).init(allocator);
|
|
3218
|
+
var src_keys = std.array_list.Managed([]const u8).init(allocator);
|
|
3105
3219
|
var src_is_map = false;
|
|
3106
3220
|
|
|
3107
3221
|
if (islist(src_raw)) {
|
|
@@ -3149,7 +3263,7 @@ fn cmdPack(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3149
3263
|
|
|
3150
3264
|
// Build the output map.
|
|
3151
3265
|
const result_obj = try allocator.create(MapRef);
|
|
3152
|
-
result_obj.* = .{ .data =
|
|
3266
|
+
result_obj.* = .{ .data = .empty, .allocator = allocator };
|
|
3153
3267
|
for (src_list.items, 0..) |src_item, idx| {
|
|
3154
3268
|
// Resolve the key for this item.
|
|
3155
3269
|
var item_key: []const u8 = "";
|
|
@@ -3158,7 +3272,7 @@ fn cmdPack(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3158
3272
|
if (std.mem.startsWith(u8, kp, "`")) {
|
|
3159
3273
|
// Backtick path: inject to resolve.
|
|
3160
3274
|
const key_store = try allocator.create(MapRef);
|
|
3161
|
-
key_store.* = .{ .data =
|
|
3275
|
+
key_store.* = .{ .data = .empty, .allocator = allocator };
|
|
3162
3276
|
if (store == .object) {
|
|
3163
3277
|
var sit = store.object.iterator();
|
|
3164
3278
|
while (sit.next()) |kv| try key_store.put(kv.key_ptr.*, kv.value_ptr.*);
|
|
@@ -3181,7 +3295,7 @@ fn cmdPack(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3181
3295
|
|
|
3182
3296
|
// Build per-item store.
|
|
3183
3297
|
const item_store = try allocator.create(MapRef);
|
|
3184
|
-
item_store.* = .{ .data =
|
|
3298
|
+
item_store.* = .{ .data = .empty, .allocator = allocator };
|
|
3185
3299
|
if (store == .object) {
|
|
3186
3300
|
var sit = store.object.iterator();
|
|
3187
3301
|
while (sit.next()) |kv| try item_store.put(kv.key_ptr.*, kv.value_ptr.*);
|
|
@@ -3193,7 +3307,7 @@ fn cmdPack(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3193
3307
|
// use the resolved item_key (supports $KEY=$COPY → "a" not "0").
|
|
3194
3308
|
if (child_clone == .object) {
|
|
3195
3309
|
const anno = try allocator.create(MapRef);
|
|
3196
|
-
anno.* = .{ .data =
|
|
3310
|
+
anno.* = .{ .data = .empty, .allocator = allocator };
|
|
3197
3311
|
const anno_val = if (src_is_map)
|
|
3198
3312
|
JsonValue{ .string = src_keys.items[idx] }
|
|
3199
3313
|
else
|
|
@@ -3204,7 +3318,7 @@ fn cmdPack(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3204
3318
|
|
|
3205
3319
|
// Build dpath for ancestor path resolution using the ORIGINAL store,
|
|
3206
3320
|
// so `...v100` can resolve back to the root data.
|
|
3207
|
-
var dpath_list = std.
|
|
3321
|
+
var dpath_list = std.array_list.Managed([]const u8).init(allocator);
|
|
3208
3322
|
try dpath_list.append(S_DTOP);
|
|
3209
3323
|
if (srcpath.len > 0) {
|
|
3210
3324
|
var spit = std.mem.splitScalar(u8, srcpath, '.');
|
|
@@ -3218,17 +3332,17 @@ fn cmdPack(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3218
3332
|
|
|
3219
3333
|
// Wrap: {tkey: {src_key: src_item}} so dparent descent works.
|
|
3220
3334
|
const inner_wrap = try allocator.create(MapRef);
|
|
3221
|
-
inner_wrap.* = .{ .data =
|
|
3335
|
+
inner_wrap.* = .{ .data = .empty, .allocator = allocator };
|
|
3222
3336
|
try inner_wrap.put(src_keys.items[idx], src_item);
|
|
3223
3337
|
const outer_wrap = try allocator.create(MapRef);
|
|
3224
|
-
outer_wrap.* = .{ .data =
|
|
3338
|
+
outer_wrap.* = .{ .data = .empty, .allocator = allocator };
|
|
3225
3339
|
try outer_wrap.put(tkey, JsonValue{ .object = inner_wrap });
|
|
3226
3340
|
|
|
3227
3341
|
// Build per-item store: merge original root data keys into the
|
|
3228
3342
|
// store alongside $TOP = src_item. This way `$COPY` gets the item
|
|
3229
3343
|
// (via $TOP) and `...v100` can find v100 as a direct store key.
|
|
3230
3344
|
const pack_store = try allocator.create(MapRef);
|
|
3231
|
-
pack_store.* = .{ .data =
|
|
3345
|
+
pack_store.* = .{ .data = .empty, .allocator = allocator };
|
|
3232
3346
|
if (store == .object) {
|
|
3233
3347
|
var sit = store.object.iterator();
|
|
3234
3348
|
while (sit.next()) |kv| try pack_store.put(kv.key_ptr.*, kv.value_ptr.*);
|
|
@@ -3333,7 +3447,7 @@ fn cmdRef(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3333
3447
|
|
|
3334
3448
|
// Resolve data at these paths.
|
|
3335
3449
|
const tcur = if (cpath.len > 0) blk: {
|
|
3336
|
-
var joined = std.
|
|
3450
|
+
var joined = std.array_list.Managed(u8).init(allocator);
|
|
3337
3451
|
for (cpath, 0..) |p, pi| {
|
|
3338
3452
|
if (pi > 0) try joined.append('.');
|
|
3339
3453
|
try joined.appendSlice(p);
|
|
@@ -3379,7 +3493,7 @@ fn cmdRef(allocator: Allocator, inj: *Injection, store: JsonValue) !JsonValue {
|
|
|
3379
3493
|
tpath;
|
|
3380
3494
|
const tval_at: JsonValue = blk2: {
|
|
3381
3495
|
if (tpath_strip.len == 0) break :blk2 store;
|
|
3382
|
-
var joined = std.
|
|
3496
|
+
var joined = std.array_list.Managed(u8).init(allocator);
|
|
3383
3497
|
for (tpath_strip, 0..) |p, pi| {
|
|
3384
3498
|
if (pi > 0) joined.append('.') catch break :blk2 .null;
|
|
3385
3499
|
joined.appendSlice(p) catch break :blk2 .null;
|
|
@@ -3475,7 +3589,7 @@ pub fn checkPlacement(
|
|
|
3475
3589
|
inj: *Injection,
|
|
3476
3590
|
) !bool {
|
|
3477
3591
|
if ((modes & inj.mode) == 0) {
|
|
3478
|
-
var expected = std.
|
|
3592
|
+
var expected = std.array_list.Managed(u8).init(allocator);
|
|
3479
3593
|
var first = true;
|
|
3480
3594
|
for ([_]i32{ M_KEYPRE, M_KEYPOST, M_VAL }) |m| {
|
|
3481
3595
|
if ((modes & m) != 0) {
|
|
@@ -3530,7 +3644,7 @@ fn injectChild(allocator: Allocator, child_raw: JsonValue, store: JsonValue, inj
|
|
|
3530
3644
|
|
|
3531
3645
|
// Build store with correct data context.
|
|
3532
3646
|
const child_store = try allocator.create(MapRef);
|
|
3533
|
-
child_store.* = .{ .data =
|
|
3647
|
+
child_store.* = .{ .data = .empty, .allocator = allocator };
|
|
3534
3648
|
if (store == .object) {
|
|
3535
3649
|
var sit = store.object.iterator();
|
|
3536
3650
|
while (sit.next()) |kv| try child_store.put(kv.key_ptr.*, kv.value_ptr.*);
|
|
@@ -3546,8 +3660,15 @@ fn injectChild(allocator: Allocator, child_raw: JsonValue, store: JsonValue, inj
|
|
|
3546
3660
|
// Transform — public API. Builds store and calls Inject.
|
|
3547
3661
|
// ============================================================================
|
|
3548
3662
|
|
|
3549
|
-
|
|
3550
|
-
|
|
3663
|
+
/// Canonical `transform` collects injection errors and throws
|
|
3664
|
+
/// `join(errs, ' | ')` when any were collected. This port passed `null` for
|
|
3665
|
+
/// the injection, so no `errs` list existed, nothing was ever collected and
|
|
3666
|
+
/// nothing could be reported - `transform/apply` and `transform/format` had
|
|
3667
|
+
/// no way to produce the messages the corpus asserts. The error channel is
|
|
3668
|
+
/// the same one `validate` already uses: a message beside the value, since
|
|
3669
|
+
/// a Zig error carries no payload.
|
|
3670
|
+
pub fn transform(allocator: Allocator, data: JsonValue, spec: JsonValue) anyerror!struct { out: JsonValue, err: ?[]const u8 } {
|
|
3671
|
+
if (spec == .null) return .{ .out = spec, .err = null };
|
|
3551
3672
|
|
|
3552
3673
|
const spec_clone = try clone(allocator, spec);
|
|
3553
3674
|
const data_clone = if (data == .null) JsonValue{ .null = {} } else try clone(allocator, data);
|
|
@@ -3556,12 +3677,38 @@ pub fn transform(allocator: Allocator, data: JsonValue, spec: JsonValue) !JsonVa
|
|
|
3556
3677
|
const orig_spec = try clone(allocator, spec);
|
|
3557
3678
|
|
|
3558
3679
|
const store = try allocator.create(MapRef);
|
|
3559
|
-
store.* = .{ .data =
|
|
3680
|
+
store.* = .{ .data = .empty, .allocator = allocator };
|
|
3560
3681
|
try store.put(S_DTOP, data_clone);
|
|
3561
3682
|
try store.put(S_DSPEC, orig_spec);
|
|
3683
|
+
|
|
3684
|
+
const errs = try allocator.create(std.array_list.Managed([]const u8));
|
|
3685
|
+
errs.* = std.array_list.Managed([]const u8).init(allocator);
|
|
3686
|
+
try store.put(S_DERRS, JsonValue{ .null = {} });
|
|
3687
|
+
|
|
3562
3688
|
const store_val = JsonValue{ .object = store };
|
|
3563
3689
|
|
|
3564
|
-
|
|
3690
|
+
const inj_init = try allocator.create(Injection);
|
|
3691
|
+
inj_init.* = Injection{
|
|
3692
|
+
.allocator = allocator,
|
|
3693
|
+
.mode = 0, // triggers root init
|
|
3694
|
+
.keys = try allocator.alloc([]const u8, 0),
|
|
3695
|
+
.path = try allocator.alloc([]const u8, 0),
|
|
3696
|
+
.nodes = try allocator.alloc(JsonValue, 0),
|
|
3697
|
+
.dpath = try allocator.alloc([]const u8, 0),
|
|
3698
|
+
.errs = errs,
|
|
3699
|
+
};
|
|
3700
|
+
|
|
3701
|
+
const result = try inject(allocator, spec_clone, store_val, inj_init);
|
|
3702
|
+
|
|
3703
|
+
if (0 < errs.items.len) {
|
|
3704
|
+
var msg = std.array_list.Managed(u8).init(allocator);
|
|
3705
|
+
for (errs.items, 0..) |e, i| {
|
|
3706
|
+
if (0 < i) try msg.appendSlice(" | ");
|
|
3707
|
+
try msg.appendSlice(e);
|
|
3708
|
+
}
|
|
3709
|
+
return .{ .out = result, .err = msg.items };
|
|
3710
|
+
}
|
|
3711
|
+
return .{ .out = result, .err = null };
|
|
3565
3712
|
}
|
|
3566
3713
|
|
|
3567
3714
|
// Transform with a modify callback applied after each injection step.
|
|
@@ -3573,7 +3720,7 @@ pub fn transformModify(allocator: Allocator, data: JsonValue, spec: JsonValue, m
|
|
|
3573
3720
|
const orig_spec = try clone(allocator, spec);
|
|
3574
3721
|
|
|
3575
3722
|
const store = try allocator.create(MapRef);
|
|
3576
|
-
store.* = .{ .data =
|
|
3723
|
+
store.* = .{ .data = .empty, .allocator = allocator };
|
|
3577
3724
|
try store.put(S_DTOP, data_clone);
|
|
3578
3725
|
try store.put(S_DSPEC, orig_spec);
|
|
3579
3726
|
const store_val = JsonValue{ .object = store };
|
|
@@ -3584,8 +3731,8 @@ pub fn transformModify(allocator: Allocator, data: JsonValue, spec: JsonValue, m
|
|
|
3584
3731
|
const empty_path = try allocator.alloc([]const u8, 0);
|
|
3585
3732
|
const empty_nodes = try allocator.alloc(JsonValue, 0);
|
|
3586
3733
|
const empty_dpath = try allocator.alloc([]const u8, 0);
|
|
3587
|
-
const errs = try allocator.create(std.
|
|
3588
|
-
errs.* = std.
|
|
3734
|
+
const errs = try allocator.create(std.array_list.Managed([]const u8));
|
|
3735
|
+
errs.* = std.array_list.Managed([]const u8).init(allocator);
|
|
3589
3736
|
inj_init.* = Injection{
|
|
3590
3737
|
.allocator = allocator,
|
|
3591
3738
|
.mode = 0, // triggers root initialization in inject
|
|
@@ -3615,7 +3762,7 @@ fn resolveSpecialEscapes(allocator: Allocator, pathref: []const u8) []const u8 {
|
|
|
3615
3762
|
if (pathref.len <= 3) return pathref;
|
|
3616
3763
|
if (std.mem.indexOf(u8, pathref, "$BT") == null and
|
|
3617
3764
|
std.mem.indexOf(u8, pathref, "$DS") == null) return pathref;
|
|
3618
|
-
var result = std.
|
|
3765
|
+
var result = std.array_list.Managed(u8).init(allocator);
|
|
3619
3766
|
var i: usize = 0;
|
|
3620
3767
|
while (i < pathref.len) {
|
|
3621
3768
|
if (i + 3 <= pathref.len and std.mem.eql(u8, pathref[i .. i + 3], "$BT")) {
|
|
@@ -3639,22 +3786,34 @@ fn resolveSpecialEscapes(allocator: Allocator, pathref: []const u8) []const u8 {
|
|
|
3639
3786
|
// Uses the transform/inject infrastructure with type-checking commands.
|
|
3640
3787
|
// ============================================================================
|
|
3641
3788
|
|
|
3642
|
-
|
|
3789
|
+
// NAMED, not anonymous. Two `struct { ... }` written out separately are two
|
|
3790
|
+
// DISTINCT types in Zig even when their fields match, so while `validate` and
|
|
3791
|
+
// `validateWith` each declared their own, `validate`'s one-line delegation
|
|
3792
|
+
// could not compile: "error union payload cannot cast into error union
|
|
3793
|
+
// payload". Zig analyses lazily, so nothing noticed until a caller appeared -
|
|
3794
|
+
// the SDK's zig target was the first. Fails identically on 0.13 and 0.16.
|
|
3795
|
+
pub const ValidateResult = struct { out: JsonValue, err: ?[]const u8 };
|
|
3796
|
+
|
|
3797
|
+
pub fn validate(allocator: Allocator, data: JsonValue, spec: JsonValue) anyerror!ValidateResult {
|
|
3798
|
+
return validateWith(allocator, data, spec, .null);
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
pub fn validateWith(allocator: Allocator, data: JsonValue, spec: JsonValue, injdef: JsonValue) anyerror!ValidateResult {
|
|
3643
3802
|
const spec_clone = try clone(allocator, spec);
|
|
3644
3803
|
const data_clone = if (data == .null) JsonValue{ .null = {} } else try clone(allocator, data);
|
|
3645
3804
|
const orig_spec = try clone(allocator, spec);
|
|
3646
3805
|
|
|
3647
3806
|
// Build store with data and spec.
|
|
3648
3807
|
const store = try allocator.create(MapRef);
|
|
3649
|
-
store.* = .{ .data =
|
|
3808
|
+
store.* = .{ .data = .empty, .allocator = allocator };
|
|
3650
3809
|
try store.put(S_DTOP, data_clone);
|
|
3651
3810
|
try store.put(S_DSPEC, orig_spec);
|
|
3652
3811
|
const store_val = JsonValue{ .object = store };
|
|
3653
3812
|
|
|
3654
3813
|
// Create root injection with validate_mode enabled.
|
|
3655
3814
|
// This causes dispatchCmd to handle $STRING, $NUMBER, etc.
|
|
3656
|
-
const errs = try allocator.create(std.
|
|
3657
|
-
errs.* = std.
|
|
3815
|
+
const errs = try allocator.create(std.array_list.Managed([]const u8));
|
|
3816
|
+
errs.* = std.array_list.Managed([]const u8).init(allocator);
|
|
3658
3817
|
|
|
3659
3818
|
const inj_init = try allocator.create(Injection);
|
|
3660
3819
|
inj_init.* = Injection{
|
|
@@ -3668,12 +3827,23 @@ pub fn validate(allocator: Allocator, data: JsonValue, spec: JsonValue) anyerror
|
|
|
3668
3827
|
.dpath = try allocator.alloc([]const u8, 0),
|
|
3669
3828
|
.errs = errs,
|
|
3670
3829
|
};
|
|
3830
|
+
if (injdef == .object) {
|
|
3831
|
+
inj_init.meta = injdef.object.get("meta") orelse .null;
|
|
3832
|
+
if (inj_init.meta == .object) {
|
|
3833
|
+
if (inj_init.meta.object.get(S_BEXACT)) |exact| {
|
|
3834
|
+
inj_init.exact_mode = exact == .bool and exact.bool;
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3671
3838
|
|
|
3672
3839
|
const result = try inject(allocator, spec_clone, store_val, inj_init);
|
|
3673
3840
|
|
|
3674
3841
|
if (errs.items.len > 0) {
|
|
3675
|
-
|
|
3676
|
-
|
|
3842
|
+
// Canonical is `throw new Error(join(errs, ' | '))` - the collected
|
|
3843
|
+
// messages and nothing else. This port prefixed "Invalid data: ",
|
|
3844
|
+
// which no other port emits and which the corpus's validate/invalid
|
|
3845
|
+
// entries do not expect.
|
|
3846
|
+
var msg = std.array_list.Managed(u8).init(allocator);
|
|
3677
3847
|
for (errs.items, 0..) |e, i| {
|
|
3678
3848
|
if (i > 0) try msg.appendSlice(" | ");
|
|
3679
3849
|
try msg.appendSlice(e);
|
|
@@ -3728,17 +3898,22 @@ fn validationModify(allocator: Allocator, _: JsonValue, key: []const u8, parent:
|
|
|
3728
3898
|
// Closed: report unexpected keys.
|
|
3729
3899
|
const ckeys = keysof(allocator, cval) catch return;
|
|
3730
3900
|
if (ckeys == .array) {
|
|
3731
|
-
var bad = std.
|
|
3901
|
+
var bad = std.array_list.Managed([]const u8).init(allocator);
|
|
3732
3902
|
for (ckeys.array.data.items) |ck| {
|
|
3733
3903
|
if (ck != .string) continue;
|
|
3734
|
-
|
|
3904
|
+
// Literal presence: the shape DECLARES ck even when its value
|
|
3905
|
+
// is JSON null. Canonical uses `NONE === _lookup`; haskey is
|
|
3906
|
+
// Group A (value-based) and would drop records with a null
|
|
3907
|
+
// field from an open ($AND) select. Test object key presence.
|
|
3908
|
+
const present = (pval == .object) and (pval.object.get(ck.string) != null);
|
|
3909
|
+
if (!present) {
|
|
3735
3910
|
bad.append(ck.string) catch {};
|
|
3736
3911
|
}
|
|
3737
3912
|
}
|
|
3738
3913
|
if (bad.items.len > 0) {
|
|
3739
|
-
var badmsg = std.
|
|
3914
|
+
var badmsg = std.array_list.Managed(u8).init(allocator);
|
|
3740
3915
|
badmsg.appendSlice("Unexpected keys at field ") catch {};
|
|
3741
|
-
badmsg.appendSlice(pathifySlice(allocator,
|
|
3916
|
+
badmsg.appendSlice(pathifySlice(allocator, inj.path[1..]) catch "<root>") catch {};
|
|
3742
3917
|
badmsg.appendSlice(": ") catch {};
|
|
3743
3918
|
for (bad.items, 0..) |bk, bi| {
|
|
3744
3919
|
if (bi > 0) badmsg.appendSlice(", ") catch {};
|
|
@@ -3759,8 +3934,11 @@ fn validationModify(allocator: Allocator, _: JsonValue, key: []const u8, parent:
|
|
|
3759
3934
|
const a = toStdJson(allocator, cval) catch return;
|
|
3760
3935
|
const b = toStdJson(allocator, pval) catch return;
|
|
3761
3936
|
if (!stdJsonEqual(a, b)) {
|
|
3762
|
-
const
|
|
3763
|
-
|
|
3937
|
+
const pathmsg = if (1 < inj.path.len)
|
|
3938
|
+
std.fmt.allocPrint(allocator, "at field {s}: ", .{pathifySlice(allocator, inj.path[1..]) catch "<root>"}) catch ""
|
|
3939
|
+
else
|
|
3940
|
+
"";
|
|
3941
|
+
inj.errs.append(std.fmt.allocPrint(allocator, "Value {s}{s} should equal {s}.", .{ pathmsg, stringify(allocator, cval, null) catch "?", stringify(allocator, pval, null) catch "?" }) catch "exact match error") catch {};
|
|
3764
3942
|
}
|
|
3765
3943
|
} else {
|
|
3766
3944
|
// Non-exact: copy data value into spec result.
|
|
@@ -3773,7 +3951,7 @@ fn validateWalk(
|
|
|
3773
3951
|
allocator: Allocator,
|
|
3774
3952
|
spec_val: JsonValue,
|
|
3775
3953
|
data_val: JsonValue,
|
|
3776
|
-
errs: *std.
|
|
3954
|
+
errs: *std.array_list.Managed([]const u8),
|
|
3777
3955
|
path: []const []const u8,
|
|
3778
3956
|
) anyerror!JsonValue {
|
|
3779
3957
|
// Type command strings: `$STRING`, `$NUMBER` etc resolve to type checks.
|
|
@@ -3869,7 +4047,7 @@ fn validateTypeCheck(
|
|
|
3869
4047
|
allocator: Allocator,
|
|
3870
4048
|
cmd: []const u8,
|
|
3871
4049
|
data_val: JsonValue,
|
|
3872
|
-
errs: *std.
|
|
4050
|
+
errs: *std.array_list.Managed([]const u8),
|
|
3873
4051
|
path: []const []const u8,
|
|
3874
4052
|
) anyerror!JsonValue {
|
|
3875
4053
|
const t = typify(data_val);
|
|
@@ -3939,12 +4117,12 @@ fn validateOne(
|
|
|
3939
4117
|
allocator: Allocator,
|
|
3940
4118
|
spec_val: JsonValue,
|
|
3941
4119
|
data_val: JsonValue,
|
|
3942
|
-
errs: *std.
|
|
4120
|
+
errs: *std.array_list.Managed([]const u8),
|
|
3943
4121
|
path: []const []const u8,
|
|
3944
4122
|
) anyerror!JsonValue {
|
|
3945
4123
|
const alts = spec_val.array.data.items[1..];
|
|
3946
4124
|
for (alts) |alt| {
|
|
3947
|
-
var terrs = std.
|
|
4125
|
+
var terrs = std.array_list.Managed([]const u8).init(allocator);
|
|
3948
4126
|
_ = try validateWalk(allocator, alt, data_val, &terrs, path);
|
|
3949
4127
|
if (terrs.items.len == 0) return data_val;
|
|
3950
4128
|
}
|
|
@@ -3958,7 +4136,7 @@ fn validateExact(
|
|
|
3958
4136
|
allocator: Allocator,
|
|
3959
4137
|
spec_val: JsonValue,
|
|
3960
4138
|
data_val: JsonValue,
|
|
3961
|
-
errs: *std.
|
|
4139
|
+
errs: *std.array_list.Managed([]const u8),
|
|
3962
4140
|
path: []const []const u8,
|
|
3963
4141
|
) anyerror!JsonValue {
|
|
3964
4142
|
const alts = spec_val.array.data.items[1..];
|
|
@@ -3981,7 +4159,7 @@ fn validateChild(
|
|
|
3981
4159
|
allocator: Allocator,
|
|
3982
4160
|
spec_val: JsonValue,
|
|
3983
4161
|
data_val: JsonValue,
|
|
3984
|
-
errs: *std.
|
|
4162
|
+
errs: *std.array_list.Managed([]const u8),
|
|
3985
4163
|
path: []const []const u8,
|
|
3986
4164
|
) anyerror!JsonValue {
|
|
3987
4165
|
// [$CHILD, template] — validate each element of data array.
|
|
@@ -4006,7 +4184,7 @@ fn validateChildMap(
|
|
|
4006
4184
|
allocator: Allocator,
|
|
4007
4185
|
child_spec: JsonValue,
|
|
4008
4186
|
data_val: JsonValue,
|
|
4009
|
-
errs: *std.
|
|
4187
|
+
errs: *std.array_list.Managed([]const u8),
|
|
4010
4188
|
path: []const []const u8,
|
|
4011
4189
|
) anyerror!JsonValue {
|
|
4012
4190
|
if (data_val != .object) {
|
|
@@ -4023,22 +4201,43 @@ fn validateChildMap(
|
|
|
4023
4201
|
return data_val;
|
|
4024
4202
|
}
|
|
4025
4203
|
|
|
4204
|
+
/// Canonical `_invalidTypeMsg`:
|
|
4205
|
+
///
|
|
4206
|
+
/// 'Expected ' +
|
|
4207
|
+
/// (1 < size(path) ? 'field ' + pathify(path, 1) + ' to be ' : '') +
|
|
4208
|
+
/// needtype + ', but found ' +
|
|
4209
|
+
/// (null != v ? typename(vt) + S_VIZ : '') + vs + '.'
|
|
4210
|
+
///
|
|
4211
|
+
/// Three things this port had wrong, all of them invisible while the driver
|
|
4212
|
+
/// discarded validate's message: the needtype was emitted TWICE (once before
|
|
4213
|
+
/// the field clause and once inside it); the path was rendered whole, so
|
|
4214
|
+
/// every message carried the `$TOP` root element canonical drops with
|
|
4215
|
+
/// `pathify(path, 1)`; and a null value was reported as `null: null` rather
|
|
4216
|
+
/// than `no value`, and only at the root.
|
|
4026
4217
|
fn invalidTypeMsg(allocator: Allocator, path: []const []const u8, expected: []const u8, val: JsonValue) anyerror![]const u8 {
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4218
|
+
var buf = std.array_list.Managed(u8).init(allocator);
|
|
4219
|
+
try buf.appendSlice("Expected ");
|
|
4220
|
+
if (1 < path.len) {
|
|
4221
|
+
try buf.appendSlice("field ");
|
|
4222
|
+
try buf.appendSlice(try pathifySlice(allocator, path[1..]));
|
|
4223
|
+
try buf.appendSlice(" to be ");
|
|
4224
|
+
}
|
|
4225
|
+
try buf.appendSlice(expected);
|
|
4226
|
+
try buf.appendSlice(", but found ");
|
|
4227
|
+
if (val == .null) {
|
|
4228
|
+
try buf.appendSlice("no value");
|
|
4229
|
+
} else {
|
|
4230
|
+
try buf.appendSlice(typename(typify(val)));
|
|
4231
|
+
try buf.appendSlice(S_VIZ);
|
|
4232
|
+
try buf.appendSlice(try stringify(allocator, val, 33));
|
|
4035
4233
|
}
|
|
4036
|
-
|
|
4234
|
+
try buf.append('.');
|
|
4235
|
+
return buf.items;
|
|
4037
4236
|
}
|
|
4038
4237
|
|
|
4039
4238
|
fn pathifySlice(allocator: Allocator, path: []const []const u8) anyerror![]const u8 {
|
|
4040
4239
|
if (path.len == 0) return "<root>";
|
|
4041
|
-
var buf = std.
|
|
4240
|
+
var buf = std.array_list.Managed(u8).init(allocator);
|
|
4042
4241
|
for (path, 0..) |p, i| {
|
|
4043
4242
|
if (i > 0) try buf.append('.');
|
|
4044
4243
|
try buf.appendSlice(p);
|
|
@@ -4094,7 +4293,7 @@ pub fn select(allocator: Allocator, children: JsonValue, query: JsonValue) anyer
|
|
|
4094
4293
|
if (!isnode(children)) return try JsonValue.makeList(allocator);
|
|
4095
4294
|
|
|
4096
4295
|
// Normalize children: add $KEY for map/list items.
|
|
4097
|
-
var child_list = std.
|
|
4296
|
+
var child_list = std.array_list.Managed(JsonValue).init(allocator);
|
|
4098
4297
|
|
|
4099
4298
|
if (ismap(children)) {
|
|
4100
4299
|
const pairs = try items(allocator, children);
|
|
@@ -4124,7 +4323,7 @@ pub fn select(allocator: Allocator, children: JsonValue, query: JsonValue) anyer
|
|
|
4124
4323
|
result_lr.* = .{ .data = ListData.init(allocator) };
|
|
4125
4324
|
|
|
4126
4325
|
for (child_list.items) |child| {
|
|
4127
|
-
var terrs = std.
|
|
4326
|
+
var terrs = std.array_list.Managed([]const u8).init(allocator);
|
|
4128
4327
|
const q = try clone(allocator, query);
|
|
4129
4328
|
|
|
4130
4329
|
// Mark all maps in query as open.
|
|
@@ -4152,7 +4351,7 @@ fn validateExactMatch(
|
|
|
4152
4351
|
allocator: Allocator,
|
|
4153
4352
|
spec_val: JsonValue,
|
|
4154
4353
|
data_val: JsonValue,
|
|
4155
|
-
errs: *std.
|
|
4354
|
+
errs: *std.array_list.Managed([]const u8),
|
|
4156
4355
|
path: []const []const u8,
|
|
4157
4356
|
) anyerror!JsonValue {
|
|
4158
4357
|
// Operator handling for select queries. Operators ($AND/$OR/$NOT/$CMP)
|
|
@@ -4303,12 +4502,12 @@ fn selectAnd(
|
|
|
4303
4502
|
allocator: Allocator,
|
|
4304
4503
|
terms: JsonValue,
|
|
4305
4504
|
data_val: JsonValue,
|
|
4306
|
-
errs: *std.
|
|
4505
|
+
errs: *std.array_list.Managed([]const u8),
|
|
4307
4506
|
path: []const []const u8,
|
|
4308
4507
|
) anyerror!JsonValue {
|
|
4309
4508
|
if (terms != .array) return data_val;
|
|
4310
4509
|
for (terms.array.data.items) |term| {
|
|
4311
|
-
var terrs = std.
|
|
4510
|
+
var terrs = std.array_list.Managed([]const u8).init(allocator);
|
|
4312
4511
|
_ = try validateExactMatch(allocator, term, data_val, &terrs, path);
|
|
4313
4512
|
if (terrs.items.len > 0) {
|
|
4314
4513
|
try errs.append("AND condition failed");
|
|
@@ -4322,12 +4521,12 @@ fn selectOr(
|
|
|
4322
4521
|
allocator: Allocator,
|
|
4323
4522
|
terms: JsonValue,
|
|
4324
4523
|
data_val: JsonValue,
|
|
4325
|
-
errs: *std.
|
|
4524
|
+
errs: *std.array_list.Managed([]const u8),
|
|
4326
4525
|
path: []const []const u8,
|
|
4327
4526
|
) anyerror!JsonValue {
|
|
4328
4527
|
if (terms != .array) return data_val;
|
|
4329
4528
|
for (terms.array.data.items) |term| {
|
|
4330
|
-
var terrs = std.
|
|
4529
|
+
var terrs = std.array_list.Managed([]const u8).init(allocator);
|
|
4331
4530
|
_ = try validateExactMatch(allocator, term, data_val, &terrs, path);
|
|
4332
4531
|
if (terrs.items.len == 0) return data_val;
|
|
4333
4532
|
}
|
|
@@ -4339,10 +4538,10 @@ fn selectNot(
|
|
|
4339
4538
|
allocator: Allocator,
|
|
4340
4539
|
term: JsonValue,
|
|
4341
4540
|
data_val: JsonValue,
|
|
4342
|
-
errs: *std.
|
|
4541
|
+
errs: *std.array_list.Managed([]const u8),
|
|
4343
4542
|
path: []const []const u8,
|
|
4344
4543
|
) anyerror!JsonValue {
|
|
4345
|
-
var terrs = std.
|
|
4544
|
+
var terrs = std.array_list.Managed([]const u8).init(allocator);
|
|
4346
4545
|
_ = try validateExactMatch(allocator, term, data_val, &terrs, path);
|
|
4347
4546
|
if (terrs.items.len == 0) {
|
|
4348
4547
|
try errs.append("NOT: condition should not have matched");
|
|
@@ -4355,7 +4554,7 @@ fn selectCmp(
|
|
|
4355
4554
|
op: []const u8,
|
|
4356
4555
|
term: JsonValue,
|
|
4357
4556
|
data_val: JsonValue,
|
|
4358
|
-
errs: *std.
|
|
4557
|
+
errs: *std.array_list.Managed([]const u8),
|
|
4359
4558
|
path: []const []const u8,
|
|
4360
4559
|
) anyerror!JsonValue {
|
|
4361
4560
|
_ = path;
|