@voxgig/sdkgen 4.2.3 → 4.2.4
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/src/cmp/rust/ReadmeEntity_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeHowto_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeIntro_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeModel_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeQuick_rust.ts +3 -3
- package/project/.sdk/src/cmp/rust/ReadmeRef_rust.ts +3 -3
- package/project/.sdk/src/cmp/rust/ReadmeTopQuick_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeTopTest_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +7 -7
- package/project/.sdk/tm/elixir/lib/utility/struct/voxgig_struct.ex +66 -12
- package/project/.sdk/tm/zig/Makefile +20 -0
- package/project/.sdk/tm/zig/feature/test.zig +1 -1
- package/project/sdkgen-package.json +1 -1
package/bin/voxgig-sdkgen
CHANGED
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getModelPath,
|
|
7
7
|
} from '@voxgig/apidef'
|
|
8
8
|
|
|
9
|
-
import { rustVarName } from './utility_rust'
|
|
9
|
+
import { rustVarName, rustMethodName } from './utility_rust'
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
// Type names come from the shared canonToType 'rust' column (single source of truth).
|
|
@@ -56,7 +56,7 @@ const ReadmeEntity = cmp(function ReadmeEntity(props: any) {
|
|
|
56
56
|
const fields = entity.fields || []
|
|
57
57
|
const idF = entityIdField(entity)
|
|
58
58
|
const eVar = rustVarName(entity.name)
|
|
59
|
-
const method =
|
|
59
|
+
const method = rustMethodName(entity.name)
|
|
60
60
|
|
|
61
61
|
Content(`
|
|
62
62
|
### ${entity.Name}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
nom,
|
|
8
8
|
} from '@voxgig/apidef'
|
|
9
9
|
|
|
10
|
-
import { crateIdent, rustVarName } from './utility_rust'
|
|
10
|
+
import { crateIdent, rustVarName, rustMethodName } from './utility_rust'
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
// A type-correct rust expression constructing a voxgig struct Value.
|
|
@@ -31,7 +31,7 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
|
|
|
31
31
|
const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
|
|
32
32
|
const eName = exampleEntity ? nom(exampleEntity, 'Name') : 'Entity'
|
|
33
33
|
const eVar = exampleEntity ? rustVarName(exampleEntity.name) : 'entity'
|
|
34
|
-
const method = exampleEntity ?
|
|
34
|
+
const method = exampleEntity ? rustMethodName(exampleEntity.name) : 'entity'
|
|
35
35
|
const idF = exampleEntity ? entityIdField(exampleEntity) : null
|
|
36
36
|
const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
|
|
37
37
|
let testArg = 'Value::Noval'
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getModelPath,
|
|
7
7
|
} from '@voxgig/apidef'
|
|
8
8
|
|
|
9
|
-
import { rustVarName } from './utility_rust'
|
|
9
|
+
import { rustVarName, rustMethodName } from './utility_rust'
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
const ReadmeIntro = cmp(function ReadmeIntro(props: any) {
|
|
@@ -30,7 +30,7 @@ The ${target.title} SDK for the ${model.Name} API — an entity-oriented client
|
|
|
30
30
|
const exampleEntity = Object.values(entity || {})
|
|
31
31
|
.find((e: any) => e.active !== false) as any
|
|
32
32
|
const example = exampleEntity
|
|
33
|
-
? `for example \`client.${
|
|
33
|
+
? `for example \`client.${rustMethodName(exampleEntity.name)}(Value::Noval)\` — each`
|
|
34
34
|
: 'each'
|
|
35
35
|
|
|
36
36
|
// Model-driven op list — only the operations the active entities actually
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getModelPath,
|
|
7
7
|
} from '@voxgig/apidef'
|
|
8
8
|
|
|
9
|
-
import { crateIdent, rustVarName } from './utility_rust'
|
|
9
|
+
import { crateIdent, rustVarName, rustMethodName } from './utility_rust'
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
const ReadmeModel = cmp(function ReadmeModel(props: any) {
|
|
@@ -77,7 +77,7 @@ Creates a test-mode client with mock transport. Both arguments may be
|
|
|
77
77
|
|
|
78
78
|
each(entityList, (ent: any) => {
|
|
79
79
|
const article = /^[aeiou]/i.test(ent.Name) ? 'an' : 'a'
|
|
80
|
-
Content(`| \`${
|
|
80
|
+
Content(`| \`${rustMethodName(ent.name)}\` | \`(entopts: Value) -> Rc<${ent.Name}Entity>\` | Create ${article} ${ent.Name} entity instance. |
|
|
81
81
|
`)
|
|
82
82
|
})
|
|
83
83
|
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
nom,
|
|
8
8
|
} from '@voxgig/apidef'
|
|
9
9
|
|
|
10
|
-
import { crateIdent, rustVarName } from './utility_rust'
|
|
10
|
+
import { crateIdent, rustVarName, rustMethodName } from './utility_rust'
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
const ReadmeQuick = cmp(function ReadmeQuick(props: any) {
|
|
@@ -58,7 +58,7 @@ let client = ${ctor};
|
|
|
58
58
|
const eName = nom(exampleEntity, 'Name')
|
|
59
59
|
const article = /^[aeiou]/i.test(eName) ? 'an' : 'a'
|
|
60
60
|
const eVar = rustVarName(exampleEntity.name)
|
|
61
|
-
const method =
|
|
61
|
+
const method = rustMethodName(exampleEntity.name)
|
|
62
62
|
const opnames = entityOps(exampleEntity)
|
|
63
63
|
// Model-driven id key: `idF` is the entity's id-like MATCH field name, or
|
|
64
64
|
// null. `dataIdF` is the id on the RETURNED record's data type.
|
|
@@ -91,7 +91,7 @@ match client.${method}(Value::Noval).list(Value::Noval, Value::Noval) {
|
|
|
91
91
|
const neName = nom(nestedEntity, 'Name')
|
|
92
92
|
const neArticle = /^[aeiou]/i.test(neName) ? 'an' : 'a'
|
|
93
93
|
const neVar = rustVarName(nestedEntity.name)
|
|
94
|
-
const neMethod =
|
|
94
|
+
const neMethod = rustMethodName(nestedEntity.name)
|
|
95
95
|
|
|
96
96
|
const neIdF = entityIdField(nestedEntity)
|
|
97
97
|
const neRequired = opRequestShape(nestedEntity, 'load').items
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getModelPath,
|
|
7
7
|
} from '@voxgig/apidef'
|
|
8
8
|
|
|
9
|
-
import { crateIdent, rustVarName } from './utility_rust'
|
|
9
|
+
import { crateIdent, rustVarName, rustMethodName } from './utility_rust'
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
// Type names come from the shared canonToType 'rust' column (single source of truth).
|
|
@@ -120,7 +120,7 @@ let client = test_sdk(Value::Noval, Value::Noval);
|
|
|
120
120
|
|
|
121
121
|
// Entity factory methods
|
|
122
122
|
publishedEntities.map((ent: any) => {
|
|
123
|
-
Content(`#### \`${
|
|
123
|
+
Content(`#### \`${rustMethodName(ent.name)}(entopts: Value) -> Rc<${ent.Name}Entity>\`
|
|
124
124
|
|
|
125
125
|
Create a new \`${ent.Name}Entity\` instance. Pass \`Value::Noval\` for no
|
|
126
126
|
initial options.
|
|
@@ -168,7 +168,7 @@ Prepare a fetch definition without sending. Returns the fetchdef on \`Ok\`.
|
|
|
168
168
|
const fields = ent.fields || []
|
|
169
169
|
const idF = entityIdField(ent)
|
|
170
170
|
const eVar = rustVarName(ent.name)
|
|
171
|
-
const method =
|
|
171
|
+
const method = rustMethodName(ent.name)
|
|
172
172
|
|
|
173
173
|
Content(`
|
|
174
174
|
---
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
nom,
|
|
8
8
|
} from '@voxgig/apidef'
|
|
9
9
|
|
|
10
|
-
import { crateIdent, rustVarName } from './utility_rust'
|
|
10
|
+
import { crateIdent, rustVarName, rustMethodName } from './utility_rust'
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
// A type-correct rust expression constructing a voxgig struct Value for a
|
|
@@ -46,7 +46,7 @@ let client = ${ctor};
|
|
|
46
46
|
if (exampleEntity) {
|
|
47
47
|
const eName = nom(exampleEntity, 'Name')
|
|
48
48
|
const eVar = rustVarName(exampleEntity.name)
|
|
49
|
-
const method =
|
|
49
|
+
const method = rustMethodName(exampleEntity.name)
|
|
50
50
|
const opnames = Object.keys(exampleEntity.op || {})
|
|
51
51
|
// Model-driven id key: null when the entity has no id-like field, in which
|
|
52
52
|
// case the load example takes no match argument.
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
nom,
|
|
8
8
|
} from '@voxgig/apidef'
|
|
9
9
|
|
|
10
|
-
import { crateIdent, rustVarName } from './utility_rust'
|
|
10
|
+
import { crateIdent, rustVarName, rustMethodName } from './utility_rust'
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
// A type-correct rust expression constructing a voxgig struct Value for a
|
|
@@ -40,7 +40,7 @@ let client = test_sdk(Value::Noval, Value::Noval);
|
|
|
40
40
|
|
|
41
41
|
if (exampleEntity && primaryOp) {
|
|
42
42
|
const eName = nom(exampleEntity, 'Name')
|
|
43
|
-
const method =
|
|
43
|
+
const method = rustMethodName(exampleEntity.name)
|
|
44
44
|
const idF = entityIdField(exampleEntity)
|
|
45
45
|
const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
|
|
46
46
|
let arg = 'Value::Noval'
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from '@voxgig/sdkgen'
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
import { rustVarName } from './utility_rust'
|
|
24
|
+
import { rustVarName, rustMethodName } from './utility_rust'
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
// Rust's GenCtx mirrors the shared shape (see TestEntity_ts.ts) plus the
|
|
@@ -100,7 +100,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
|
|
|
100
100
|
}
|
|
101
101
|
`
|
|
102
102
|
|
|
103
|
-
const method =
|
|
103
|
+
const method = rustMethodName(entity.name)
|
|
104
104
|
const evar = rustVarName(entity.name)
|
|
105
105
|
|
|
106
106
|
File({ name: entity.name + '_entity_test.' + target.ext }, () => {
|
|
@@ -330,7 +330,7 @@ const generateCreate: OpGen = (ctx, step, index) => {
|
|
|
330
330
|
Content(` // CREATE
|
|
331
331
|
`)
|
|
332
332
|
if (needsEnt) {
|
|
333
|
-
Content(` let ${entvar} = client.${
|
|
333
|
+
Content(` let ${entvar} = client.${rustMethodName(entity.name)}(Value::Noval);
|
|
334
334
|
`)
|
|
335
335
|
}
|
|
336
336
|
|
|
@@ -381,7 +381,7 @@ const generateList: OpGen = (ctx, step, index) => {
|
|
|
381
381
|
Content(` // LIST
|
|
382
382
|
`)
|
|
383
383
|
if (needsEnt) {
|
|
384
|
-
Content(` let ${entvar} = client.${
|
|
384
|
+
Content(` let ${entvar} = client.${rustMethodName(entity.name)}(Value::Noval);
|
|
385
385
|
`)
|
|
386
386
|
}
|
|
387
387
|
|
|
@@ -464,7 +464,7 @@ const generateUpdate: OpGen = (ctx, step, index) => {
|
|
|
464
464
|
Content(` // UPDATE
|
|
465
465
|
`)
|
|
466
466
|
if (needsEnt) {
|
|
467
|
-
Content(` let ${entvar} = client.${
|
|
467
|
+
Content(` let ${entvar} = client.${rustMethodName(entity.name)}(Value::Noval);
|
|
468
468
|
`)
|
|
469
469
|
}
|
|
470
470
|
Content(` let ${datavar}_up = Value::empty_map();
|
|
@@ -568,7 +568,7 @@ const generateLoad: OpGen = (ctx, step, index) => {
|
|
|
568
568
|
Content(` // LOAD
|
|
569
569
|
`)
|
|
570
570
|
if (!hasEntVar) {
|
|
571
|
-
Content(` let ${entvar} = client.${
|
|
571
|
+
Content(` let ${entvar} = client.${rustMethodName(entity.name)}(Value::Noval);
|
|
572
572
|
`)
|
|
573
573
|
}
|
|
574
574
|
if (!hasSrcData && hasEntId) {
|
|
@@ -626,7 +626,7 @@ const generateRemove: OpGen = (ctx, step, index) => {
|
|
|
626
626
|
Content(` // REMOVE
|
|
627
627
|
`)
|
|
628
628
|
if (needsEnt) {
|
|
629
|
-
Content(` let ${entvar} = client.${
|
|
629
|
+
Content(` let ${entvar} = client.${rustMethodName(entity.name)}(Value::Noval);
|
|
630
630
|
`)
|
|
631
631
|
}
|
|
632
632
|
// Always match the prior-created entity by id to avoid mock-order flakes.
|
|
@@ -26,17 +26,74 @@ defmodule Voxgig.Struct do
|
|
|
26
26
|
# ---------------------------------------------------------------------------
|
|
27
27
|
|
|
28
28
|
@heap :vox_struct_heap
|
|
29
|
-
|
|
29
|
+
@heap_owner :vox_struct_heap_owner
|
|
30
|
+
|
|
31
|
+
# A named ETS table is owned by the process that created it and dies with
|
|
32
|
+
# that process. The values here are HANDLES into it - {:vmap, id} - and they
|
|
33
|
+
# are passed across process boundaries freely: ExUnit runs every test in its
|
|
34
|
+
# own process, and a generated SDK caches its config in :persistent_term,
|
|
35
|
+
# which outlives all of them. So the table has to outlive its creator, or a
|
|
36
|
+
# handle that is still very much alive resolves into a fresh empty table.
|
|
37
|
+
#
|
|
38
|
+
# Adding ensure_heap to hget stopped that raising ArgumentError, but a silent
|
|
39
|
+
# nil turned out to be worse than the raise: the generated config's staleness
|
|
40
|
+
# check (`usable?`) was written to read the raise as "rebuild me", so once the
|
|
41
|
+
# raise stopped, a dead handle was cached as good and clone/1 crashed further
|
|
42
|
+
# down with "Enumerable not implemented for nil of type Atom" - nothing in the
|
|
43
|
+
# message pointing at the heap.
|
|
44
|
+
#
|
|
45
|
+
# The table is therefore owned by a dedicated process that never exits, and
|
|
46
|
+
# every id stays valid for the life of the VM.
|
|
30
47
|
defp ensure_heap do
|
|
31
48
|
case :ets.whereis(@heap) do
|
|
32
|
-
:undefined ->
|
|
49
|
+
:undefined -> start_heap()
|
|
50
|
+
_ -> :ok
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
defp start_heap do
|
|
55
|
+
caller = self()
|
|
56
|
+
|
|
57
|
+
spawn(fn ->
|
|
58
|
+
owner =
|
|
33
59
|
try do
|
|
34
|
-
|
|
60
|
+
Process.register(self(), @heap_owner)
|
|
61
|
+
true
|
|
35
62
|
rescue
|
|
36
|
-
ArgumentError ->
|
|
63
|
+
ArgumentError -> false
|
|
37
64
|
end
|
|
38
65
|
|
|
39
|
-
|
|
66
|
+
if owner do
|
|
67
|
+
case :ets.whereis(@heap) do
|
|
68
|
+
:undefined -> :ets.new(@heap, [:set, :public, :named_table])
|
|
69
|
+
_ -> :ok
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
send(caller, :vox_heap_ready)
|
|
74
|
+
|
|
75
|
+
# Hold the table for the life of the VM. This process does nothing else.
|
|
76
|
+
if owner, do: Process.sleep(:infinity)
|
|
77
|
+
end)
|
|
78
|
+
|
|
79
|
+
receive do
|
|
80
|
+
:vox_heap_ready -> :ok
|
|
81
|
+
after
|
|
82
|
+
5_000 -> :ok
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
await_heap(1_000)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# The loser of a registration race reports ready before the winner has
|
|
89
|
+
# necessarily reached :ets.new, so wait on the table, not on the message.
|
|
90
|
+
defp await_heap(0), do: :ok
|
|
91
|
+
|
|
92
|
+
defp await_heap(n) do
|
|
93
|
+
case :ets.whereis(@heap) do
|
|
94
|
+
:undefined ->
|
|
95
|
+
Process.sleep(1)
|
|
96
|
+
await_heap(n - 1)
|
|
40
97
|
|
|
41
98
|
_ ->
|
|
42
99
|
:ok
|
|
@@ -50,13 +107,10 @@ defmodule Voxgig.Struct do
|
|
|
50
107
|
id
|
|
51
108
|
end
|
|
52
109
|
|
|
53
|
-
# ensure_heap here too, not only in alloc
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
# to an existing ETS table") from whichever unrelated test happened to run
|
|
58
|
-
# second, which is why this surfaced as a pipeline/netsim failure rather than
|
|
59
|
-
# as anything to do with the heap.
|
|
110
|
+
# ensure_heap here too, not only in alloc, so a read can never hit a missing
|
|
111
|
+
# table. With a permanent owner (above) that should not arise; before there
|
|
112
|
+
# was one, it raised ArgumentError from whichever unrelated test happened to
|
|
113
|
+
# run second.
|
|
60
114
|
defp hget(id) do
|
|
61
115
|
ensure_heap()
|
|
62
116
|
|
|
@@ -8,10 +8,30 @@ build:
|
|
|
8
8
|
# tests pass, due to *MapRef/*ListRef cross-references in arena teardown. We
|
|
9
9
|
# filter the output: if "N/N tests passed" with N==total, treat as success.
|
|
10
10
|
# `--summary all` is required so the count is printed.
|
|
11
|
+
#
|
|
12
|
+
# A TEST THAT DID NOT COMPILE IS NOT IN THE TALLY. The tolerance above is for a
|
|
13
|
+
# crash AFTER the tests ran, and it must never cover a build that failed: a
|
|
14
|
+
# compile error means those test binaries never ran, so they never reach
|
|
15
|
+
# "total", and "60/66 passed; 6 skipped" reads as a clean 66/66 while five of
|
|
16
|
+
# thirteen build steps are red. That is exactly how a `.undef` tag that does
|
|
17
|
+
# not exist in JsonValue shipped in 4.2.2 and stayed green in every sweep
|
|
18
|
+
# afterwards. So: if any build step failed, this target fails, whatever the
|
|
19
|
+
# test counts say.
|
|
11
20
|
test:
|
|
12
21
|
@out=$$(zig build test --summary all 2>&1); code=$$?; \
|
|
13
22
|
echo "$$out"; \
|
|
14
23
|
if [ $$code -eq 0 ]; then exit 0; fi; \
|
|
24
|
+
steps=$$(echo "$$out" | grep -oE '[0-9]+/[0-9]+ steps succeeded' | tail -1); \
|
|
25
|
+
sdone=$$(echo "$$steps" | sed -E 's#([0-9]+)/([0-9]+).*#\1#'); \
|
|
26
|
+
stotal=$$(echo "$$steps" | sed -E 's#([0-9]+)/([0-9]+).*#\2#'); \
|
|
27
|
+
if [ -n "$$steps" ] && [ "$$sdone" != "$$stotal" ]; then \
|
|
28
|
+
echo "zig: $$steps — a build step failed, so the test tally is incomplete"; \
|
|
29
|
+
exit 1; \
|
|
30
|
+
fi; \
|
|
31
|
+
if echo "$$out" | grep -q 'error: the following command failed with .* compilation errors'; then \
|
|
32
|
+
echo "zig: compilation errors — not a teardown crash"; \
|
|
33
|
+
exit 1; \
|
|
34
|
+
fi; \
|
|
15
35
|
summary=$$(echo "$$out" | grep -oE '[0-9]+/[0-9]+ tests passed(; [0-9]+ skipped)?' | tail -1); \
|
|
16
36
|
passed=$$(echo "$$summary" | sed -E 's#([0-9]+)/([0-9]+).*#\1#'); \
|
|
17
37
|
total=$$(echo "$$summary" | sed -E 's#([0-9]+)/([0-9]+).*#\2#'); \
|
|
@@ -105,7 +105,7 @@ fn fixIds(_: Allocator, key: ?[]const u8, val: Value, _: Value, path: []const []
|
|
|
105
105
|
// while the fixture plainly held two. Mirrors the go/ts/lua/php mocks, which
|
|
106
106
|
// already wrap; rust, c and zig were the three that did not.
|
|
107
107
|
fn envelope(ctx: *Context, data: Value) Value {
|
|
108
|
-
if (data == .
|
|
108
|
+
if (data == .null) return data;
|
|
109
109
|
const restf = h.getpath(&.{ "transform", "res" }, ctx.point);
|
|
110
110
|
if (restf != .string) return data;
|
|
111
111
|
const spec = restf.string;
|