snowflake-code-unit-registry 0.4.3__tar.gz → 0.4.10__tar.gz
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.
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/Cargo.lock +56 -12
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/Cargo.toml +1 -1
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/PKG-INFO +1 -1
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/Cargo.toml +3 -2
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/build.rs +210 -161
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/examples/code-unit.example.json +1 -1
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/schemas/code-unit.schema.json +11 -10
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/checksum.rs +15 -29
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/error.rs +11 -22
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/filter.rs +13 -14
- snowflake_code_unit_registry-0.4.10/crates/scai-state-core/src/generated/file_path_fields.rs +9 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/generated/mod.rs +1 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/generated/query_reference.rs +5 -5
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/lib.rs +6 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/registry/loader.rs +1 -6
- snowflake_code_unit_registry-0.4.10/crates/scai-state-core/src/registry/paths.rs +208 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/registry/tests.rs +350 -12
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/registry.rs +72 -35
- snowflake_code_unit_registry-0.4.10/crates/scai-state-core/src/validation.rs +328 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/src/lib.rs +25 -0
- snowflake_code_unit_registry-0.4.10/crates/scai-state-python/tests/test_validation.py +90 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/pyproject.toml +1 -1
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/python/snowflake_code_unit_registry/__init__.py +68 -1
- snowflake_code_unit_registry-0.4.10/python/snowflake_code_unit_registry/types.py +375 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/README.md +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/schemas/query-reference.rs.tmpl +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/schemas/query-reference.tmpl +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/generated/updated_at_paths.rs +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/identity.rs +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/registry/README.md +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/registry/graph.rs +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/registry/in_memory.rs +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/registry/query.rs +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-core/src/registry/test_helpers.rs +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/Cargo.toml +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/README.md +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/tests/conftest.py +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/tests/test_batch.py +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/tests/test_checksum.py +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/tests/test_crud.py +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/tests/test_errors.py +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/tests/test_query.py +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/tests/test_refresh.py +0 -0
- {snowflake_code_unit_registry-0.4.3 → snowflake_code_unit_registry-0.4.10}/crates/scai-state-python/tests/test_serialization.py +0 -0
|
@@ -1192,9 +1192,9 @@ dependencies = [
|
|
|
1192
1192
|
|
|
1193
1193
|
[[package]]
|
|
1194
1194
|
name = "jsonschema"
|
|
1195
|
-
version = "0.
|
|
1195
|
+
version = "0.45.0"
|
|
1196
1196
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1197
|
-
checksum = "
|
|
1197
|
+
checksum = "6f29616f6e19415398eb186964fb7cbbeef572c79bede3622a8277667924bbe3"
|
|
1198
1198
|
dependencies = [
|
|
1199
1199
|
"ahash",
|
|
1200
1200
|
"bytecount",
|
|
@@ -1852,9 +1852,9 @@ dependencies = [
|
|
|
1852
1852
|
|
|
1853
1853
|
[[package]]
|
|
1854
1854
|
name = "referencing"
|
|
1855
|
-
version = "0.
|
|
1855
|
+
version = "0.45.0"
|
|
1856
1856
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1857
|
-
checksum = "
|
|
1857
|
+
checksum = "b8a618c14f8ba29d8193bb55e2bf13e4fb2b1115313ecb7ae94b43100c7ac7d5"
|
|
1858
1858
|
dependencies = [
|
|
1859
1859
|
"ahash",
|
|
1860
1860
|
"fluent-uri",
|
|
@@ -1904,6 +1904,16 @@ dependencies = [
|
|
|
1904
1904
|
"memchr",
|
|
1905
1905
|
]
|
|
1906
1906
|
|
|
1907
|
+
[[package]]
|
|
1908
|
+
name = "regress"
|
|
1909
|
+
version = "0.11.0"
|
|
1910
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1911
|
+
checksum = "07948de9abc2e83adbeb7543c061a5ddaf7d944afcafbdd6e6b39aeacd40504b"
|
|
1912
|
+
dependencies = [
|
|
1913
|
+
"hashbrown 0.16.1",
|
|
1914
|
+
"memchr",
|
|
1915
|
+
]
|
|
1916
|
+
|
|
1907
1917
|
[[package]]
|
|
1908
1918
|
name = "reqwest"
|
|
1909
1919
|
version = "0.13.2"
|
|
@@ -2067,7 +2077,7 @@ dependencies = [
|
|
|
2067
2077
|
|
|
2068
2078
|
[[package]]
|
|
2069
2079
|
name = "scai-state-core"
|
|
2070
|
-
version = "0.4.
|
|
2080
|
+
version = "0.4.10"
|
|
2071
2081
|
dependencies = [
|
|
2072
2082
|
"chrono",
|
|
2073
2083
|
"err_code",
|
|
@@ -2075,7 +2085,7 @@ dependencies = [
|
|
|
2075
2085
|
"jsonschema",
|
|
2076
2086
|
"prettyplease",
|
|
2077
2087
|
"rayon",
|
|
2078
|
-
"regress",
|
|
2088
|
+
"regress 0.11.0",
|
|
2079
2089
|
"schemars",
|
|
2080
2090
|
"serde",
|
|
2081
2091
|
"serde_json",
|
|
@@ -2083,6 +2093,7 @@ dependencies = [
|
|
|
2083
2093
|
"strum",
|
|
2084
2094
|
"syn",
|
|
2085
2095
|
"tempfile",
|
|
2096
|
+
"test-case",
|
|
2086
2097
|
"thiserror 2.0.18",
|
|
2087
2098
|
"typify",
|
|
2088
2099
|
"uuid",
|
|
@@ -2091,7 +2102,7 @@ dependencies = [
|
|
|
2091
2102
|
|
|
2092
2103
|
[[package]]
|
|
2093
2104
|
name = "scai-state-csharp"
|
|
2094
|
-
version = "0.4.
|
|
2105
|
+
version = "0.4.10"
|
|
2095
2106
|
dependencies = [
|
|
2096
2107
|
"interoptopus",
|
|
2097
2108
|
"interoptopus_backend_csharp",
|
|
@@ -2102,7 +2113,7 @@ dependencies = [
|
|
|
2102
2113
|
|
|
2103
2114
|
[[package]]
|
|
2104
2115
|
name = "scai-state-node"
|
|
2105
|
-
version = "0.4.
|
|
2116
|
+
version = "0.4.10"
|
|
2106
2117
|
dependencies = [
|
|
2107
2118
|
"napi",
|
|
2108
2119
|
"napi-build",
|
|
@@ -2114,7 +2125,7 @@ dependencies = [
|
|
|
2114
2125
|
|
|
2115
2126
|
[[package]]
|
|
2116
2127
|
name = "scai-state-python"
|
|
2117
|
-
version = "0.4.
|
|
2128
|
+
version = "0.4.10"
|
|
2118
2129
|
dependencies = [
|
|
2119
2130
|
"pyo3",
|
|
2120
2131
|
"pythonize",
|
|
@@ -2417,9 +2428,9 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
2417
2428
|
|
|
2418
2429
|
[[package]]
|
|
2419
2430
|
name = "tempfile"
|
|
2420
|
-
version = "3.
|
|
2431
|
+
version = "3.27.0"
|
|
2421
2432
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2422
|
-
checksum = "
|
|
2433
|
+
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
|
2423
2434
|
dependencies = [
|
|
2424
2435
|
"fastrand",
|
|
2425
2436
|
"getrandom 0.4.2",
|
|
@@ -2450,6 +2461,39 @@ dependencies = [
|
|
|
2450
2461
|
"unicode-segmentation",
|
|
2451
2462
|
]
|
|
2452
2463
|
|
|
2464
|
+
[[package]]
|
|
2465
|
+
name = "test-case"
|
|
2466
|
+
version = "3.3.1"
|
|
2467
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2468
|
+
checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8"
|
|
2469
|
+
dependencies = [
|
|
2470
|
+
"test-case-macros",
|
|
2471
|
+
]
|
|
2472
|
+
|
|
2473
|
+
[[package]]
|
|
2474
|
+
name = "test-case-core"
|
|
2475
|
+
version = "3.3.1"
|
|
2476
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2477
|
+
checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f"
|
|
2478
|
+
dependencies = [
|
|
2479
|
+
"cfg-if",
|
|
2480
|
+
"proc-macro2",
|
|
2481
|
+
"quote",
|
|
2482
|
+
"syn",
|
|
2483
|
+
]
|
|
2484
|
+
|
|
2485
|
+
[[package]]
|
|
2486
|
+
name = "test-case-macros"
|
|
2487
|
+
version = "3.3.1"
|
|
2488
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2489
|
+
checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
|
|
2490
|
+
dependencies = [
|
|
2491
|
+
"proc-macro2",
|
|
2492
|
+
"quote",
|
|
2493
|
+
"syn",
|
|
2494
|
+
"test-case-core",
|
|
2495
|
+
]
|
|
2496
|
+
|
|
2453
2497
|
[[package]]
|
|
2454
2498
|
name = "thiserror"
|
|
2455
2499
|
version = "1.0.69"
|
|
@@ -2633,7 +2677,7 @@ dependencies = [
|
|
|
2633
2677
|
"log",
|
|
2634
2678
|
"proc-macro2",
|
|
2635
2679
|
"quote",
|
|
2636
|
-
"regress",
|
|
2680
|
+
"regress 0.10.5",
|
|
2637
2681
|
"schemars",
|
|
2638
2682
|
"semver",
|
|
2639
2683
|
"serde",
|
|
@@ -14,10 +14,11 @@ strum.workspace = true
|
|
|
14
14
|
uuid.workspace = true
|
|
15
15
|
fs2.workspace = true
|
|
16
16
|
sqlparser.workspace = true
|
|
17
|
-
regress = "0.
|
|
17
|
+
regress = "0.11"
|
|
18
18
|
chrono = { version = "0.4", features = ["serde"] }
|
|
19
19
|
xxhash-rust = { version = "0.8", features = ["xxh3"] }
|
|
20
20
|
rayon = "1.10"
|
|
21
|
+
jsonschema = "0.45"
|
|
21
22
|
|
|
22
23
|
[build-dependencies]
|
|
23
24
|
typify = "0.6"
|
|
@@ -28,4 +29,4 @@ prettyplease = "0.2"
|
|
|
28
29
|
|
|
29
30
|
[dev-dependencies]
|
|
30
31
|
tempfile = "3"
|
|
31
|
-
|
|
32
|
+
test-case = "3"
|
|
@@ -1,6 +1,104 @@
|
|
|
1
1
|
use std::fs;
|
|
2
2
|
use std::path::Path;
|
|
3
3
|
|
|
4
|
+
const DEFINITION_REF_PREFIX: &str = "#/definitions/";
|
|
5
|
+
|
|
6
|
+
// ── Schema Document ──────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
type JsonObject = serde_json::Map<String, serde_json::Value>;
|
|
9
|
+
|
|
10
|
+
/// Parsed JSON Schema with centralized navigation helpers.
|
|
11
|
+
/// Created once and shared across all generators so the schema is parsed
|
|
12
|
+
/// and its `definitions` map resolved in a single place.
|
|
13
|
+
struct SchemaDoc {
|
|
14
|
+
root: serde_json::Value,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl SchemaDoc {
|
|
18
|
+
fn from_str(text: &str) -> Self {
|
|
19
|
+
let root: serde_json::Value =
|
|
20
|
+
serde_json::from_str(text).expect("Failed to parse schema JSON");
|
|
21
|
+
SchemaDoc { root }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fn definitions(&self) -> Option<&JsonObject> {
|
|
25
|
+
self.root.get("definitions").and_then(|d| d.as_object())
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Resolve a `$ref` node to its target definition.
|
|
29
|
+
/// Non-ref nodes pass through unchanged.
|
|
30
|
+
/// Panics on malformed or unresolvable refs — those are schema-authoring
|
|
31
|
+
/// errors that should fail the build loudly.
|
|
32
|
+
fn resolve<'a>(&'a self, node: &'a serde_json::Value) -> &'a serde_json::Value {
|
|
33
|
+
let Some(ref_path) = node.get("$ref").and_then(|r| r.as_str()) else {
|
|
34
|
+
return node;
|
|
35
|
+
};
|
|
36
|
+
let def_name = ref_path
|
|
37
|
+
.strip_prefix(DEFINITION_REF_PREFIX)
|
|
38
|
+
.unwrap_or_else(|| panic!("Unsupported $ref format: {ref_path}"));
|
|
39
|
+
self.definitions()
|
|
40
|
+
.and_then(|d| d.get(def_name))
|
|
41
|
+
.unwrap_or_else(|| panic!("Unresolved $ref: {ref_path}"))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Sorted property entries of a JSON Schema object node.
|
|
45
|
+
/// Returns an empty vec for non-objects or objects without a `properties` key.
|
|
46
|
+
fn sorted_properties<'a>(
|
|
47
|
+
&'a self,
|
|
48
|
+
node: &'a serde_json::Value,
|
|
49
|
+
) -> Vec<(&'a str, &'a serde_json::Value)> {
|
|
50
|
+
let Some(properties) = node.get("properties").and_then(|p| p.as_object()) else {
|
|
51
|
+
return Vec::new();
|
|
52
|
+
};
|
|
53
|
+
let mut entries: Vec<(&str, &serde_json::Value)> =
|
|
54
|
+
properties.iter().map(|(k, v)| (k.as_str(), v)).collect();
|
|
55
|
+
entries.sort_by(|(a, _), (b, _)| a.cmp(b));
|
|
56
|
+
entries
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// Schema `type` keyword, defaulting to `"object"` when absent.
|
|
60
|
+
fn node_type<'a>(&self, node: &'a serde_json::Value) -> &'a str {
|
|
61
|
+
node.get("type")
|
|
62
|
+
.and_then(|t| t.as_str())
|
|
63
|
+
.unwrap_or("object")
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fn root_properties(&self) -> Vec<(&str, &serde_json::Value)> {
|
|
67
|
+
self.sorted_properties(&self.root)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── Shared Object Tree Walker ────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
/// Recursively visit every property of every reachable object node in the
|
|
74
|
+
/// schema, resolving `$ref` along the way. Arrays and scalars are
|
|
75
|
+
/// intentionally skipped — the generated path constants are consumed by
|
|
76
|
+
/// object-only JSON navigation helpers that do not model array indices.
|
|
77
|
+
///
|
|
78
|
+
/// The `visit` callback receives the full segment path and the *resolved*
|
|
79
|
+
/// property value for every property of every reachable object.
|
|
80
|
+
fn walk_object_tree(
|
|
81
|
+
doc: &SchemaDoc,
|
|
82
|
+
current_path: &[String],
|
|
83
|
+
node: &serde_json::Value,
|
|
84
|
+
visit: &mut impl FnMut(&[String], &serde_json::Value),
|
|
85
|
+
) {
|
|
86
|
+
let node = doc.resolve(node);
|
|
87
|
+
if doc.node_type(node) != "object" {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (name, raw_child) in doc.sorted_properties(node) {
|
|
92
|
+
let child = doc.resolve(raw_child);
|
|
93
|
+
let mut child_path = current_path.to_vec();
|
|
94
|
+
child_path.push(name.to_string());
|
|
95
|
+
visit(&child_path, child);
|
|
96
|
+
walk_object_tree(doc, &child_path, child, visit);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── Main ─────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
4
102
|
fn main() {
|
|
5
103
|
println!("cargo:rerun-if-changed=./schemas/code-unit.schema.json");
|
|
6
104
|
println!("cargo:rerun-if-changed=./schemas/query-reference.tmpl");
|
|
@@ -23,29 +121,37 @@ fn main() {
|
|
|
23
121
|
let schema_content =
|
|
24
122
|
fs::read_to_string(schema_path).expect("Failed to read code-unit.schema.json");
|
|
25
123
|
|
|
124
|
+
// Typify needs its own parse (schemars::schema::RootSchema), so it
|
|
125
|
+
// still receives the raw string.
|
|
26
126
|
let generated_code = generate_with_typify(&schema_content);
|
|
127
|
+
write_generated(out_dir, "types.rs", &generated_code, "types");
|
|
27
128
|
|
|
28
|
-
let
|
|
29
|
-
fs::write(&output_path, &generated_code).expect("Failed to write generated types");
|
|
30
|
-
|
|
31
|
-
println!("cargo:warning=Generated types to {:?}", output_path);
|
|
129
|
+
let doc = SchemaDoc::from_str(&schema_content);
|
|
32
130
|
|
|
33
|
-
let
|
|
34
|
-
|
|
35
|
-
fs::
|
|
131
|
+
let query_template = fs::read_to_string("./schemas/query-reference.tmpl")
|
|
132
|
+
.expect("Failed to read schemas/query-reference.tmpl");
|
|
133
|
+
let query_rs_template = fs::read_to_string("./schemas/query-reference.rs.tmpl")
|
|
134
|
+
.expect("Failed to read schemas/query-reference.rs.tmpl");
|
|
36
135
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
136
|
+
write_generated(
|
|
137
|
+
out_dir,
|
|
138
|
+
"query_reference.rs",
|
|
139
|
+
&generate_query_reference(&doc, &query_template, &query_rs_template),
|
|
140
|
+
"query reference",
|
|
40
141
|
);
|
|
41
142
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
143
|
+
write_generated(
|
|
144
|
+
out_dir,
|
|
145
|
+
"updated_at_paths.rs",
|
|
146
|
+
&generate_updated_at_paths(&doc),
|
|
147
|
+
"updated_at_paths",
|
|
148
|
+
);
|
|
45
149
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
150
|
+
write_generated(
|
|
151
|
+
out_dir,
|
|
152
|
+
"file_path_fields.rs",
|
|
153
|
+
&generate_file_path_fields(&doc),
|
|
154
|
+
"file_path_fields",
|
|
49
155
|
);
|
|
50
156
|
|
|
51
157
|
check_sibling_generated_files();
|
|
@@ -72,6 +178,25 @@ fn check_sibling_generated_files() {
|
|
|
72
178
|
}
|
|
73
179
|
}
|
|
74
180
|
|
|
181
|
+
fn write_generated(out_dir: &Path, filename: &str, code: &str, label: &str) {
|
|
182
|
+
let output_path = out_dir.join(filename);
|
|
183
|
+
fs::write(&output_path, code).unwrap_or_else(|_| panic!("Failed to write {}", label));
|
|
184
|
+
println!("cargo:warning=Generated {} to {:?}", label, output_path);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
fn render_path_constant(const_name: &str, comment: &str, paths: &[Vec<String>]) -> String {
|
|
188
|
+
let mut code = String::from("// AUTO-GENERATED FROM JSON SCHEMA - DO NOT EDIT MANUALLY\n");
|
|
189
|
+
code.push_str(comment);
|
|
190
|
+
code.push_str("\n\n");
|
|
191
|
+
code.push_str(&format!("pub const {const_name}: &[&[&str]] = &[\n"));
|
|
192
|
+
for path in paths {
|
|
193
|
+
let segments: Vec<String> = path.iter().map(|s| format!("\"{}\"", s)).collect();
|
|
194
|
+
code.push_str(&format!(" &[{}],\n", segments.join(", ")));
|
|
195
|
+
}
|
|
196
|
+
code.push_str("];\n");
|
|
197
|
+
code
|
|
198
|
+
}
|
|
199
|
+
|
|
75
200
|
// ── Query Reference Generator ────────────────────────────────────────────
|
|
76
201
|
|
|
77
202
|
/// A flattened field descriptor extracted from the JSON Schema.
|
|
@@ -84,30 +209,19 @@ struct FieldInfo {
|
|
|
84
209
|
|
|
85
210
|
/// Walk the JSON Schema and produce a Rust source file containing
|
|
86
211
|
/// `pub const QUERY_REFERENCE: &str` with the full flattened field reference.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
212
|
+
///
|
|
213
|
+
/// This generator has its own recursive traversal (`collect_fields`) because
|
|
214
|
+
/// it handles arrays, enums, and `x-query-help` exclusion — none of which
|
|
215
|
+
/// apply to the path-oriented generators that share `walk_object_tree`.
|
|
216
|
+
fn generate_query_reference(doc: &SchemaDoc, template: &str, rs_template: &str) -> String {
|
|
92
217
|
let mut fields = Vec::new();
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
sorted_keys.sort();
|
|
96
|
-
for name in sorted_keys {
|
|
97
|
-
let prop = &properties[name];
|
|
98
|
-
collect_fields(&mut fields, name, prop, definitions);
|
|
99
|
-
}
|
|
218
|
+
for (name, prop) in doc.root_properties() {
|
|
219
|
+
collect_fields(&mut fields, name, prop, doc);
|
|
100
220
|
}
|
|
101
221
|
|
|
102
222
|
let fields_table = format_fields_table(&fields);
|
|
103
|
-
|
|
104
|
-
let template = fs::read_to_string("./schemas/query-reference.tmpl")
|
|
105
|
-
.expect("Failed to read schemas/query-reference.tmpl");
|
|
106
223
|
let reference = template.replace("{fields_table}", &fields_table);
|
|
107
|
-
|
|
108
224
|
let ref_literal = format!("r#\"{}\"#", reference);
|
|
109
|
-
let rs_template = fs::read_to_string("./schemas/query-reference.rs.tmpl")
|
|
110
|
-
.expect("Failed to read schemas/query-reference.rs.tmpl");
|
|
111
225
|
rs_template.replace("{ref_literal}", &ref_literal)
|
|
112
226
|
}
|
|
113
227
|
|
|
@@ -124,26 +238,15 @@ fn collect_fields(
|
|
|
124
238
|
fields: &mut Vec<FieldInfo>,
|
|
125
239
|
prefix: &str,
|
|
126
240
|
prop: &serde_json::Value,
|
|
127
|
-
|
|
241
|
+
doc: &SchemaDoc,
|
|
128
242
|
) {
|
|
129
243
|
if is_excluded(prop) {
|
|
130
244
|
return;
|
|
131
245
|
}
|
|
132
246
|
|
|
133
|
-
|
|
134
|
-
if let Some(ref_path) = prop.get("$ref").and_then(|r| r.as_str()) {
|
|
135
|
-
if let Some(def_name) = ref_path.strip_prefix("#/definitions/") {
|
|
136
|
-
if let Some(resolved) = definitions.and_then(|d| d.get(def_name)) {
|
|
137
|
-
collect_fields(fields, prefix, resolved, definitions);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
247
|
+
let prop = doc.resolve(prop);
|
|
142
248
|
|
|
143
|
-
let type_str = prop
|
|
144
|
-
.get("type")
|
|
145
|
-
.and_then(|t| t.as_str())
|
|
146
|
-
.unwrap_or("object");
|
|
249
|
+
let type_str = doc.node_type(prop);
|
|
147
250
|
let description = prop
|
|
148
251
|
.get("description")
|
|
149
252
|
.and_then(|d| d.as_str())
|
|
@@ -152,34 +255,21 @@ fn collect_fields(
|
|
|
152
255
|
|
|
153
256
|
match type_str {
|
|
154
257
|
"object" => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
sorted_keys.sort();
|
|
165
|
-
for name in sorted_keys {
|
|
166
|
-
let sub_prop = &sub_props[name];
|
|
167
|
-
let child_path = format!("{}.{}", prefix, name);
|
|
168
|
-
collect_fields(fields, &child_path, sub_prop, definitions);
|
|
169
|
-
}
|
|
170
|
-
} else {
|
|
171
|
-
fields.push(FieldInfo {
|
|
172
|
-
path: prefix.to_string(),
|
|
173
|
-
type_name: "object".to_string(),
|
|
174
|
-
enum_values: None,
|
|
175
|
-
description,
|
|
176
|
-
});
|
|
258
|
+
fields.push(FieldInfo {
|
|
259
|
+
path: prefix.to_string(),
|
|
260
|
+
type_name: "object".to_string(),
|
|
261
|
+
enum_values: None,
|
|
262
|
+
description,
|
|
263
|
+
});
|
|
264
|
+
for (name, sub_prop) in doc.sorted_properties(prop) {
|
|
265
|
+
let child_path = format!("{}.{}", prefix, name);
|
|
266
|
+
collect_fields(fields, &child_path, sub_prop, doc);
|
|
177
267
|
}
|
|
178
268
|
}
|
|
179
269
|
"array" => {
|
|
180
270
|
let item_desc = prop
|
|
181
271
|
.get("items")
|
|
182
|
-
.
|
|
272
|
+
.map(|i| describe_array_items(i, doc))
|
|
183
273
|
.unwrap_or_default();
|
|
184
274
|
let full_desc = if description.is_empty() {
|
|
185
275
|
item_desc
|
|
@@ -220,32 +310,22 @@ fn collect_fields(
|
|
|
220
310
|
}
|
|
221
311
|
|
|
222
312
|
/// Produce a short description of array item shapes (for display only).
|
|
223
|
-
fn describe_array_items(
|
|
224
|
-
items
|
|
225
|
-
definitions: Option<&serde_json::Map<String, serde_json::Value>>,
|
|
226
|
-
) -> Option<String> {
|
|
227
|
-
// Resolve $ref on items
|
|
228
|
-
let resolved = if let Some(ref_path) = items.get("$ref").and_then(|r| r.as_str()) {
|
|
229
|
-
ref_path
|
|
230
|
-
.strip_prefix("#/definitions/")
|
|
231
|
-
.and_then(|name| definitions.and_then(|d| d.get(name)))
|
|
232
|
-
.unwrap_or(items)
|
|
233
|
-
} else {
|
|
234
|
-
items
|
|
235
|
-
};
|
|
313
|
+
fn describe_array_items(items: &serde_json::Value, doc: &SchemaDoc) -> String {
|
|
314
|
+
let resolved = doc.resolve(items);
|
|
236
315
|
|
|
237
316
|
match resolved.get("type").and_then(|t| t.as_str()) {
|
|
238
|
-
Some("string") =>
|
|
317
|
+
Some("string") => "string".to_string(),
|
|
239
318
|
Some("object") => {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
319
|
+
let props = doc.sorted_properties(resolved);
|
|
320
|
+
if props.is_empty() {
|
|
321
|
+
"object".to_string()
|
|
243
322
|
} else {
|
|
244
|
-
|
|
323
|
+
let keys: Vec<&str> = props.iter().map(|(k, _)| *k).collect();
|
|
324
|
+
format!("{{{}}}", keys.join(", "))
|
|
245
325
|
}
|
|
246
326
|
}
|
|
247
|
-
Some(t) =>
|
|
248
|
-
None =>
|
|
327
|
+
Some(t) => t.to_string(),
|
|
328
|
+
None => String::new(),
|
|
249
329
|
}
|
|
250
330
|
}
|
|
251
331
|
|
|
@@ -286,87 +366,56 @@ fn format_fields_table(fields: &[FieldInfo]) -> String {
|
|
|
286
366
|
|
|
287
367
|
/// Walk the JSON Schema and find all parent paths of `updatedAt` properties.
|
|
288
368
|
/// Generates a Rust source file with a constant array of those paths.
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
369
|
+
///
|
|
370
|
+
/// Uses `walk_object_tree` and simply records the parent path (all segments
|
|
371
|
+
/// except the last) whenever the walker visits a property named `updatedAt`.
|
|
372
|
+
fn generate_updated_at_paths(doc: &SchemaDoc) -> String {
|
|
294
373
|
let mut parent_paths: Vec<Vec<String>> = Vec::new();
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
for (name, prop) in properties {
|
|
300
|
-
if name == "updatedAt" {
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
collect_updated_at_parents(
|
|
304
|
-
&mut parent_paths,
|
|
305
|
-
std::slice::from_ref(name),
|
|
306
|
-
prop,
|
|
307
|
-
definitions,
|
|
308
|
-
);
|
|
374
|
+
|
|
375
|
+
walk_object_tree(doc, &[], &doc.root, &mut |path, _node| {
|
|
376
|
+
if path.last().map(|s| s.as_str()) == Some("updatedAt") {
|
|
377
|
+
parent_paths.push(path[..path.len() - 1].to_vec());
|
|
309
378
|
}
|
|
310
|
-
}
|
|
379
|
+
});
|
|
311
380
|
|
|
312
381
|
parent_paths.sort();
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
"//
|
|
316
|
-
|
|
317
|
-
)
|
|
318
|
-
|
|
319
|
-
code.push_str("pub const UPDATED_AT_PARENT_PATHS: &[&[&str]] = &[\n");
|
|
320
|
-
for path in &parent_paths {
|
|
321
|
-
let segments: Vec<String> = path.iter().map(|s| format!("\"{}\"", s)).collect();
|
|
322
|
-
code.push_str(&format!(" &[{}],\n", segments.join(", ")));
|
|
323
|
-
}
|
|
324
|
-
code.push_str("];\n");
|
|
325
|
-
|
|
326
|
-
code
|
|
382
|
+
render_path_constant(
|
|
383
|
+
"UPDATED_AT_PARENT_PATHS",
|
|
384
|
+
"// Parent paths of all `updatedAt` properties in code-unit.schema.json",
|
|
385
|
+
&parent_paths,
|
|
386
|
+
)
|
|
327
387
|
}
|
|
328
388
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
let type_str = prop
|
|
348
|
-
.get("type")
|
|
349
|
-
.and_then(|t| t.as_str())
|
|
350
|
-
.unwrap_or("object");
|
|
351
|
-
|
|
352
|
-
if type_str == "object" {
|
|
353
|
-
if let Some(sub_props) = prop.get("properties").and_then(|p| p.as_object()) {
|
|
354
|
-
if sub_props.contains_key("updatedAt") {
|
|
355
|
-
// current_path points to the parent object that contains updatedAt
|
|
356
|
-
parents.push(current_path.to_vec());
|
|
357
|
-
}
|
|
358
|
-
for (name, sub_prop) in sub_props {
|
|
359
|
-
if name == "updatedAt" {
|
|
360
|
-
continue;
|
|
389
|
+
// ── File Path Field Extraction ─────────────────────────────────────────────
|
|
390
|
+
|
|
391
|
+
/// Walk the JSON Schema and find all string properties whose description
|
|
392
|
+
/// starts with "Repo-root-relative". Generates a constant array of
|
|
393
|
+
/// segment paths.
|
|
394
|
+
///
|
|
395
|
+
/// Uses `walk_object_tree` and inspects each visited node.
|
|
396
|
+
fn generate_file_path_fields(doc: &SchemaDoc) -> String {
|
|
397
|
+
let mut paths: Vec<Vec<String>> = Vec::new();
|
|
398
|
+
|
|
399
|
+
walk_object_tree(doc, &[], &doc.root, &mut |path, node| {
|
|
400
|
+
if doc.node_type(node) == "string" {
|
|
401
|
+
if let Some(desc) = node.get("description").and_then(|d| d.as_str()) {
|
|
402
|
+
if desc.starts_with("Repo-root-relative") {
|
|
403
|
+
paths.push(path.to_vec());
|
|
361
404
|
}
|
|
362
|
-
let mut child_path = current_path.to_vec();
|
|
363
|
-
child_path.push(name.clone());
|
|
364
|
-
collect_updated_at_parents(parents, &child_path, sub_prop, definitions);
|
|
365
405
|
}
|
|
366
406
|
}
|
|
367
|
-
}
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
paths.sort();
|
|
410
|
+
render_path_constant(
|
|
411
|
+
"FILE_PATH_FIELDS",
|
|
412
|
+
"// Segment paths of all file path properties in code-unit.schema.json",
|
|
413
|
+
&paths,
|
|
414
|
+
)
|
|
368
415
|
}
|
|
369
416
|
|
|
417
|
+
// ── Typify Code Generation ───────────────────────────────────────────────
|
|
418
|
+
|
|
370
419
|
fn generate_with_typify(schema: &str) -> String {
|
|
371
420
|
use typify::{TypeSpace, TypeSpaceSettings};
|
|
372
421
|
|