Skip to content

Commit 308f2e8

Browse files
committed
feat(catalog/op_bridge): D-AR-6.2 — bridge 7 new ast::Kind variants (String/Bool/Float/Decimal/Datetime/Bytes/Uuid)
Catches up the C16c bridge to the AST surface PR #29 added on the op-surreal-ast side. Without this, any `field_type`-derived `ast::Kind::String` (etc.) in a Schema would fail to convert to `catalog::Kind` once #29 merges. # What this adds `From<ast::Kind> for CatalogKind` gains 7 arms: ast::Kind::String → CatalogKind::String ast::Kind::Bool → CatalogKind::Bool ast::Kind::Float → CatalogKind::Float ast::Kind::Decimal → CatalogKind::Decimal ast::Kind::Datetime → CatalogKind::Datetime ast::Kind::Bytes → CatalogKind::Bytes ast::Kind::Uuid → CatalogKind::Uuid All seven are 1:1 mirrors of existing `surrealdb_core::expr::kind::Kind` variants. Option-wrapping (Rails-nullable, per codex P1 fix in PR #29) flows through the pre-existing `ast::Kind::Option(inner)` arm: `ast::Kind::String.optional()` → `catalog::Kind::Either([None, String])`. # Dep pin `op-surreal-ast` git branch ref updated from `claude/op-surreal-ast-from-triples` (PR #26 era, pre-D-AR-5.2) to `claude/op-surreal-ast-field-types-stacked` (PR #29 with the new variants). Flip to `branch = "main"` after #29 merges. # Tests `+2` new under `--features op-bridge`: - `d_ar_6_2_scalar_variants_map_one_to_one` — every new ast variant maps to the expected catalog variant. - `d_ar_6_2_option_wrapped_scalar_bridges_via_either` — the Rails-nullable Option<String> → catalog Either(None, String) chain still works for the new variants. # Forward-compat status (D-AR-6.3 deferred) The `ast::FieldDefinition.assert: Option<String>` slot added in PR #27 is still ignored by the bridge (D-AR-6.3 follow-up). Lowering a SurrealQL expression string to `catalog::Expr` requires either the surrealdb-core SurrealQL parser (heavy) or a constrained mini-parser for the few expressions the AR-shape extractor emits (`$value != NONE` is the only one today). Punted to a focused PR. # Iron-rule lock Zero new types. Zero new traits. Seven additive match arms, all 1:1 with existing catalog variants. No behavioural drift on the pre-existing 4 arms (Any/Int/Record/Option).
1 parent 3aa6ab9 commit 308f2e8

3 files changed

Lines changed: 59 additions & 7 deletions

File tree

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,11 @@ lance-graph-contract = { git = "https://github.com/AdaWorldAPI/lance-graph.git"
164164
# FieldDefinition, IndexDefinition, Kind, Schema}` are the upstream input
165165
# types for the C16c bridge (the `From<op_surreal_ast::*> for catalog::*`
166166
# impls in surrealdb-core::catalog::op_bridge, gated by `op-bridge` feature).
167-
# Pinned to PR Z's branch until that PR merges; switch to `branch = "main"`
168-
# after merge. The crate is zero-async-deps (depends on lance-graph-contract
169-
# only), so pulling it in is cheap.
170-
op-surreal-ast = { git = "https://github.com/AdaWorldAPI/openproject-nexgen-rs", branch = "claude/op-surreal-ast-from-triples" }
167+
# Pinned to the D-AR-5.2 nexgen branch (PR #29) which adds the 7 new Kind
168+
# variants (String/Bool/Float/Decimal/Datetime/Bytes/Uuid) + the
169+
# `FieldDefinition.assert` slot. Flip to `branch = "main"` after PR #29
170+
# merges. The crate is zero-async-deps so the pull stays cheap.
171+
op-surreal-ast = { git = "https://github.com/AdaWorldAPI/openproject-nexgen-rs", branch = "claude/op-surreal-ast-field-types-stacked" }
171172
nix = { version = "0.30.1", default-features = false }
172173
num-traits = "0.2.19"
173174
num_cpus = "1.17.0"

surrealdb/core/src/catalog/op_bridge.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ impl From<ast::Kind> for CatalogKind {
6969
match k {
7070
ast::Kind::Any => CatalogKind::Any,
7171
ast::Kind::Int => CatalogKind::Int,
72+
// D-AR-6.2: 7 scalar variants added in PR #29 (ast crate's
73+
// D-AR-5.2). All map 1:1 to surrealdb-core's catalog Kind
74+
// variants — these were the Rails-AR types the AST didn't
75+
// surface until the `field_type` predicate landed.
76+
ast::Kind::String => CatalogKind::String,
77+
ast::Kind::Bool => CatalogKind::Bool,
78+
ast::Kind::Float => CatalogKind::Float,
79+
ast::Kind::Decimal => CatalogKind::Decimal,
80+
ast::Kind::Datetime => CatalogKind::Datetime,
81+
ast::Kind::Bytes => CatalogKind::Bytes,
82+
ast::Kind::Uuid => CatalogKind::Uuid,
7283
ast::Kind::Record(targets) => {
7384
let tables = targets.into_iter().map(TableName::from).collect();
7485
CatalogKind::Record(tables)
@@ -251,6 +262,46 @@ mod tests {
251262
assert_eq!(format!("{}", targets[0]), "Project");
252263
}
253264

265+
/// **D-AR-6.2** — the 7 scalar variants added in PR #29 map 1:1
266+
/// to surrealdb-core's catalog Kind variants.
267+
#[test]
268+
fn d_ar_6_2_scalar_variants_map_one_to_one() {
269+
let cases: Vec<(ast::Kind, CatalogKind)> = vec![
270+
(ast::Kind::String, CatalogKind::String),
271+
(ast::Kind::Bool, CatalogKind::Bool),
272+
(ast::Kind::Float, CatalogKind::Float),
273+
(ast::Kind::Decimal, CatalogKind::Decimal),
274+
(ast::Kind::Datetime, CatalogKind::Datetime),
275+
(ast::Kind::Bytes, CatalogKind::Bytes),
276+
(ast::Kind::Uuid, CatalogKind::Uuid),
277+
];
278+
for (ast_kind, expected) in cases {
279+
let bridged: CatalogKind = ast_kind.clone().into();
280+
assert_eq!(
281+
bridged, expected,
282+
"ast::Kind::{ast_kind:?} did not map to {expected:?}",
283+
);
284+
}
285+
}
286+
287+
/// **D-AR-6.2** — the option-wrapped form (Rails-nullable, per
288+
/// codex P1 PR #29 fix) bridges through correctly: an AST
289+
/// `Option<String>` becomes catalog `Either(None, String)`.
290+
#[test]
291+
fn d_ar_6_2_option_wrapped_scalar_bridges_via_either() {
292+
let ast_optional_string = ast::Kind::String.optional();
293+
let bridged: CatalogKind = ast_optional_string.into();
294+
let CatalogKind::Either(arms) = bridged else {
295+
panic!("expected Either(None, String); got non-Either");
296+
};
297+
assert_eq!(arms.len(), 2);
298+
// One arm is None, the other String.
299+
let has_none = arms.iter().any(|k| matches!(k, CatalogKind::None));
300+
let has_string = arms.iter().any(|k| matches!(k, CatalogKind::String));
301+
assert!(has_none, "Option<T> must include None arm");
302+
assert!(has_string, "Option<String> must include String arm");
303+
}
304+
254305
#[test]
255306
fn kind_option_nests_correctly() {
256307
let k: CatalogKind = ast::Kind::Int.optional().into();

0 commit comments

Comments
 (0)