From 308f2e8f126c01f5f8a8ad7712610f7d7baaf072 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 16 Jun 2026 17:56:40 +0000 Subject: [PATCH] =?UTF-8?q?feat(catalog/op=5Fbridge):=20D-AR-6.2=20?= =?UTF-8?q?=E2=80=94=20bridge=207=20new=20ast::Kind=20variants=20(String/B?= =?UTF-8?q?ool/Float/Decimal/Datetime/Bytes/Uuid)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 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 → catalog Either(None, String) chain still works for the new variants. # Forward-compat status (D-AR-6.3 deferred) The `ast::FieldDefinition.assert: Option` 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). --- Cargo.lock | 6 +-- Cargo.toml | 9 +++-- surrealdb/core/src/catalog/op_bridge.rs | 51 +++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c4aac826018b..cbb1724adda3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6190,7 +6190,7 @@ dependencies = [ [[package]] name = "lance-graph-contract" version = "0.1.0" -source = "git+https://github.com/AdaWorldAPI/openproject-nexgen-rs?branch=claude%2Fop-surreal-ast-from-triples#dd1b36f42e950bd936928d417d63f8c1cd0c943d" +source = "git+https://github.com/AdaWorldAPI/openproject-nexgen-rs?branch=claude%2Fop-surreal-ast-field-types-stacked#b024c7be1810b9ca50d1ae79999a9c64810d511d" [[package]] name = "lance-graph-contract" @@ -7471,9 +7471,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "op-surreal-ast" version = "0.1.0" -source = "git+https://github.com/AdaWorldAPI/openproject-nexgen-rs?branch=claude%2Fop-surreal-ast-from-triples#dd1b36f42e950bd936928d417d63f8c1cd0c943d" +source = "git+https://github.com/AdaWorldAPI/openproject-nexgen-rs?branch=claude%2Fop-surreal-ast-field-types-stacked#b024c7be1810b9ca50d1ae79999a9c64810d511d" dependencies = [ - "lance-graph-contract 0.1.0 (git+https://github.com/AdaWorldAPI/openproject-nexgen-rs?branch=claude%2Fop-surreal-ast-from-triples)", + "lance-graph-contract 0.1.0 (git+https://github.com/AdaWorldAPI/openproject-nexgen-rs?branch=claude%2Fop-surreal-ast-field-types-stacked)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4207d43f7bec..e68fdb924171 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -164,10 +164,11 @@ lance-graph-contract = { git = "https://github.com/AdaWorldAPI/lance-graph.git" # FieldDefinition, IndexDefinition, Kind, Schema}` are the upstream input # types for the C16c bridge (the `From for catalog::*` # impls in surrealdb-core::catalog::op_bridge, gated by `op-bridge` feature). -# Pinned to PR Z's branch until that PR merges; switch to `branch = "main"` -# after merge. The crate is zero-async-deps (depends on lance-graph-contract -# only), so pulling it in is cheap. -op-surreal-ast = { git = "https://github.com/AdaWorldAPI/openproject-nexgen-rs", branch = "claude/op-surreal-ast-from-triples" } +# Pinned to the D-AR-5.2 nexgen branch (PR #29) which adds the 7 new Kind +# variants (String/Bool/Float/Decimal/Datetime/Bytes/Uuid) + the +# `FieldDefinition.assert` slot. Flip to `branch = "main"` after PR #29 +# merges. The crate is zero-async-deps so the pull stays cheap. +op-surreal-ast = { git = "https://github.com/AdaWorldAPI/openproject-nexgen-rs", branch = "claude/op-surreal-ast-field-types-stacked" } nix = { version = "0.30.1", default-features = false } num-traits = "0.2.19" num_cpus = "1.17.0" diff --git a/surrealdb/core/src/catalog/op_bridge.rs b/surrealdb/core/src/catalog/op_bridge.rs index ce20f83718fa..88379a97dabc 100644 --- a/surrealdb/core/src/catalog/op_bridge.rs +++ b/surrealdb/core/src/catalog/op_bridge.rs @@ -69,6 +69,17 @@ impl From for CatalogKind { match k { ast::Kind::Any => CatalogKind::Any, ast::Kind::Int => CatalogKind::Int, + // D-AR-6.2: 7 scalar variants added in PR #29 (ast crate's + // D-AR-5.2). All map 1:1 to surrealdb-core's catalog Kind + // variants — these were the Rails-AR types the AST didn't + // surface until the `field_type` predicate landed. + 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, ast::Kind::Record(targets) => { let tables = targets.into_iter().map(TableName::from).collect(); CatalogKind::Record(tables) @@ -251,6 +262,46 @@ mod tests { assert_eq!(format!("{}", targets[0]), "Project"); } + /// **D-AR-6.2** — the 7 scalar variants added in PR #29 map 1:1 + /// to surrealdb-core's catalog Kind variants. + #[test] + fn d_ar_6_2_scalar_variants_map_one_to_one() { + let cases: Vec<(ast::Kind, CatalogKind)> = vec![ + (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), + ]; + for (ast_kind, expected) in cases { + let bridged: CatalogKind = ast_kind.clone().into(); + assert_eq!( + bridged, expected, + "ast::Kind::{ast_kind:?} did not map to {expected:?}", + ); + } + } + + /// **D-AR-6.2** — the option-wrapped form (Rails-nullable, per + /// codex P1 PR #29 fix) bridges through correctly: an AST + /// `Option` becomes catalog `Either(None, String)`. + #[test] + fn d_ar_6_2_option_wrapped_scalar_bridges_via_either() { + let ast_optional_string = ast::Kind::String.optional(); + let bridged: CatalogKind = ast_optional_string.into(); + let CatalogKind::Either(arms) = bridged else { + panic!("expected Either(None, String); got non-Either"); + }; + assert_eq!(arms.len(), 2); + // One arm is None, the other String. + let has_none = arms.iter().any(|k| matches!(k, CatalogKind::None)); + let has_string = arms.iter().any(|k| matches!(k, CatalogKind::String)); + assert!(has_none, "Option must include None arm"); + assert!(has_string, "Option must include String arm"); + } + #[test] fn kind_option_nests_correctly() { let k: CatalogKind = ast::Kind::Int.optional().into();