-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathOmni.java
More file actions
184 lines (157 loc) · 6.54 KB
/
Copy pathOmni.java
File metadata and controls
184 lines (157 loc) · 6.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/* Copyright (c) 2025-2026 Voxgig Ltd. MIT LICENSE. */
package voxgig.struct;
// The shared test runner comes from voxgig/omni, consumed as a local checkout
// - omni is deliberately not published to Maven Central (yet). The checkout is
// resolved the same way voxgig/sekreto's ports resolve it: $OMNI_HOME first,
// then sibling paths, taking the first directory that carries spec/fib.json.
//
// Only the tests depend on omni. The library never does: omni is a test-scoped
// Maven dependency on a system path, so `mvn package` and anything published
// from `src/Struct.java` are untouched (register 4.13).
//
// ---------------------------------------------------------------------------
// The bridge is thin, and deliberately so
// ---------------------------------------------------------------------------
//
// The two value models are ALREADY the same. struct/java parses the corpus
// with Gson and omni-java with its own parser, and both produce
// `Map<String,Object>`, `List<Object>`, `String`, `Boolean`, `Double` and
// null. Numbers agree - unlike go and csharp, where one side had integers -
// so nothing needs renormalising.
//
// One thing differs: omni marks an absent value with `Json.ABSENT` and this
// port with `Struct.UNDEF`.
//
// That matters more than it looks, because it means CONTAINERS CROSS BY
// IDENTITY. The subject receives omni's own map, not a copy of it - so when
// `setpath` rewrites the store in place, omni sees the rewrite and
// `match.args` can assert on it. Eight of `minor/setpath`'s nine entries turn
// on that, and every other statically-typed port had to write the mutation
// back by hand (go, csharp) or could not observe it at all (php, and rust
// until omni gained a mutable-argument subject).
//
// The only conversion is the sentinel, and only where it can actually appear:
// ABSENT reaches a subject just once, as a whole argument, when an entry
// carries no `in` at all. It never appears INSIDE a parsed corpus value -
// parsing produces null, never absent - so there is nothing to walk.
import com.voxgig.omni.Json;
import com.voxgig.omni.Runner;
import com.voxgig.omni.Util;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/** omni's runner, in the shape this port's tests use. */
public final class Omni {
private Omni() {}
/** Value is JSON null. */
public static final String NULLMARK = "__NULL__";
/** Value is not present. */
public static final String UNDEFMARK = "__UNDEF__";
/** Value exists. */
public static final String EXISTSMARK = "__EXISTS__";
/** Locate the voxgig/omni checkout, for the error message if nothing else. */
public static String omnihome() {
List<String> candidates = new ArrayList<>();
String env = System.getenv("OMNI_HOME");
if (null != env && !env.isEmpty()) {
candidates.add(env);
}
candidates.add("../../omni");
candidates.add("../../../omni");
candidates.add("/workspace/omni");
candidates.add("/home/user/omni");
for (String candidate : candidates) {
if (new File(candidate, "spec/fib.json").isFile()) {
return candidate;
}
}
throw new IllegalStateException("struct: voxgig/omni checkout not found - set OMNI_HOME");
}
/** The shared corpus, relative to the port directory as this port's tests run. */
public static String corpuspath() {
return new File("..", "build/test/test.json").getPath();
}
/**
* omni's model -> this port's. Only the absent sentinel differs, and it can
* only be the whole value: a parsed corpus value never contains one.
*/
public static Object tostruct(Object val) {
return Util.isabsent(val) ? Struct.UNDEF : val;
}
/**
* This port's model -> omni's. Walked, because {@code Struct.UNDEF} CAN sit
* inside a result - `getpath` leaves it in a partially-resolved node - and a
* container built by the port is its own, so rewriting it harms nothing.
*/
@SuppressWarnings("unchecked")
public static Object toomni(Object val) {
if (Struct.UNDEF == val) {
return Json.ABSENT;
}
if (val instanceof Map) {
Map<String, Object> out = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : ((Map<String, Object>) val).entrySet()) {
out.put(entry.getKey(), toomni(entry.getValue()));
}
return out;
}
if (val instanceof List) {
List<Object> out = new ArrayList<>();
for (Object entry : (List<Object>) val) {
out.add(toomni(entry));
}
return out;
}
return val;
}
/** A subject in this port's shape: one argument in, one value out. */
public interface StructSubject {
Object apply(Object input);
}
/** What the runner returns for one named spec section. */
public static final class Run {
private final Runner.RunPack pack;
/** The resolved spec section. */
public final Object spec;
private Run(Runner.RunPack pack) {
this.pack = pack;
this.spec = pack.spec;
}
/** A runner over one section of the shared corpus. */
public static Run of(String name, Runner.Provider provider) {
Runner.RunnerPack runner = Runner.makeRunner(corpuspath(), provider);
return new Run(runner.runner(name));
}
/** A runner over the `struct` section, with no provider hooks. */
public static Run of(String name) {
return of(name, new Runner.Provider());
}
/** A named group of the resolved spec. */
public Object set(String name) {
return pack.set(name);
}
/** Run one set of entries with omni's default flags. */
public void runset(Object testspec, StructSubject subject) {
runsetflags(testspec, null, subject);
}
/** Run one set of entries with an explicit `null` flag. */
public void runsetnull(Object testspec, boolean donull, StructSubject subject) {
runsetflags(testspec, Runner.flags("null", donull), subject);
}
/** Run one set of entries with explicit flags. */
public void runsetflags(Object testspec, Map<String, Object> flags, StructSubject subject) {
Runner.Subject wrapped =
null == subject
? null
: args -> toomni(subject.apply(tostruct(0 < args.length ? args[0] : Json.ABSENT)));
pack.runsetflags(testspec, null == flags ? new HashMap<>() : flags, wrapped);
}
/** Run one set against the subject the spec itself names - the client path. */
public void runsetnamed(Object testspec, Map<String, Object> flags) {
pack.runsetflags(testspec, null == flags ? new HashMap<>() : flags, null);
}
}
}