Skip to content

Commit 22387fa

Browse files
authored
Merge pull request #115 from ds1sqe/rust-core
feat: native Rust ORM for TypeDB with full Python parity
2 parents 8d6ff51 + 960237b commit 22387fa

75 files changed

Lines changed: 16779 additions & 76 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,52 @@ jobs:
4545
- name: Run unit tests
4646
run: uv run pytest tests/unit/ -v --tb=short
4747

48+
# Publish Rust crates to crates.io (dependency order)
49+
publish-crates:
50+
name: Publish Rust crates to crates.io
51+
needs: test
52+
runs-on: ubuntu-latest
53+
environment: release
54+
steps:
55+
- name: Checkout code
56+
uses: actions/checkout@v4
57+
58+
- name: Install Rust toolchain
59+
uses: dtolnay/rust-toolchain@stable
60+
61+
- name: Publish type-bridge-core-lib
62+
working-directory: type-bridge-core
63+
env:
64+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
65+
run: cargo publish -p type-bridge-core-lib
66+
67+
- name: Wait for crates.io index
68+
run: sleep 30
69+
70+
- name: Publish type-bridge-orm-derive
71+
working-directory: type-bridge-core
72+
env:
73+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
74+
run: cargo publish -p type-bridge-orm-derive
75+
76+
- name: Wait for crates.io index
77+
run: sleep 30
78+
79+
- name: Publish type-bridge-orm
80+
working-directory: type-bridge-core
81+
env:
82+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
83+
run: cargo publish -p type-bridge-orm
84+
85+
- name: Wait for crates.io index
86+
run: sleep 30
87+
88+
- name: Publish type-bridge-server
89+
working-directory: type-bridge-core
90+
env:
91+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
92+
run: cargo publish -p type-bridge-server
93+
4894
# Build type-bridge-core wheels for all platforms
4995
build-core-wheels:
5096
name: Build core wheel (${{ matrix.target }})
@@ -200,7 +246,7 @@ jobs:
200246
# Create draft GitHub Release with all artifacts
201247
github-release:
202248
name: Create GitHub Release
203-
needs: publish-python-pypi
249+
needs: [publish-python-pypi, publish-crates]
204250
runs-on: ubuntu-latest
205251
permissions:
206252
contents: write

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "type-bridge"
7-
version = "1.4.0.dev1"
7+
version = "1.4.0"
88
description = "A modern, Pythonic ORM for TypeDB with an Attribute-based API"
99
readme = "README.md"
1010
requires-python = ">=3.13"
@@ -29,7 +29,7 @@ dependencies = [
2929
"lark>=1.1.9",
3030
"jinja2>=3.1.0",
3131
"typer>=0.15.0",
32-
"type-bridge-core>=0.1.0.dev0",
32+
"type-bridge-core>=1.4.0",
3333
]
3434

3535
[project.urls]

type-bridge-core/Cargo.lock

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

type-bridge-core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[workspace]
22
resolver = "3"
3-
members = ["crates/core", "crates/python", "crates/server"]
3+
members = ["crates/core", "crates/python", "crates/server", "crates/orm", "crates/orm-derive"]
44

55
[workspace.package]
66
license = "MIT"

type-bridge-core/crates/core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "type-bridge-core-lib"
3-
version = "0.1.0-dev.0"
3+
version = "1.4.0-rc0"
44
edition = "2024"
55
description = "TypeQL AST, schema parser, query compiler, and validation engine for type-bridge"
66
license.workspace = true

type-bridge-core/crates/core/benches/compilation.rs

Lines changed: 84 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,27 @@ fn make_match_with_constraints() -> Clause {
4040
}])
4141
}
4242

43-
fn make_complex_query() -> Vec<Clause> {
44-
let match_clause = Clause::Match(vec![
43+
fn make_complex_query() -> Clause {
44+
Clause::Match(vec![
4545
Pattern::Entity {
4646
variable: "$p".to_string(),
4747
type_name: "person".to_string(),
48-
constraints: vec![Constraint::Has {
49-
attr_name: "name".to_string(),
50-
value: Value::Literal(LiteralValue {
51-
value: json!("Alice"),
52-
value_type: "string".to_string(),
53-
}),
54-
}],
48+
constraints: vec![
49+
Constraint::Has {
50+
attr_name: "name".to_string(),
51+
value: Value::Literal(LiteralValue {
52+
value: json!("Alice"),
53+
value_type: "string".to_string(),
54+
}),
55+
},
56+
Constraint::Has {
57+
attr_name: "age".to_string(),
58+
value: Value::Literal(LiteralValue {
59+
value: json!(30),
60+
value_type: "long".to_string(),
61+
}),
62+
},
63+
],
5564
is_strict: false,
5665
},
5766
Pattern::Relation {
@@ -115,49 +124,40 @@ fn make_complex_query() -> Vec<Clause> {
115124
Pattern::Entity {
116125
variable: "$d".to_string(),
117126
type_name: "department".to_string(),
118-
constraints: vec![
119-
Constraint::Has {
120-
attr_name: "budget".to_string(),
121-
value: Value::Literal(LiteralValue {
122-
value: json!(100000.0),
123-
value_type: "double".to_string(),
124-
}),
125-
},
126-
],
127+
constraints: vec![Constraint::Has {
128+
attr_name: "budget".to_string(),
129+
value: Value::Literal(LiteralValue {
130+
value: json!(100000.0),
131+
value_type: "double".to_string(),
132+
}),
133+
}],
127134
is_strict: false,
128135
},
129-
Pattern::Iid {
130-
variable: "$x".to_string(),
131-
iid: "0xabcdef".to_string(),
132-
},
133-
Pattern::Attribute {
134-
variable: "$a".to_string(),
135-
type_name: "salary".to_string(),
136-
value: Some(Value::Literal(LiteralValue {
137-
value: json!(75000),
138-
value_type: "long".to_string(),
139-
})),
140-
},
141-
]);
142-
143-
let fetch_clause = Clause::Fetch(vec![
144-
FetchItem::Attribute {
145-
key: "name".to_string(),
146-
var: "$p".to_string(),
147-
attr_name: "name".to_string(),
148-
},
149-
FetchItem::Attribute {
150-
key: "email".to_string(),
151-
var: "$p".to_string(),
152-
attr_name: "email".to_string(),
136+
Pattern::Entity {
137+
variable: "$mgr".to_string(),
138+
type_name: "manager".to_string(),
139+
constraints: vec![Constraint::Has {
140+
attr_name: "level".to_string(),
141+
value: Value::Literal(LiteralValue {
142+
value: json!(3),
143+
value_type: "long".to_string(),
144+
}),
145+
}],
146+
is_strict: false,
153147
},
154-
FetchItem::Wildcard {
155-
key: "company".to_string(),
156-
var: "$c".to_string(),
148+
Pattern::Entity {
149+
variable: "$proj".to_string(),
150+
type_name: "project".to_string(),
151+
constraints: vec![Constraint::Has {
152+
attr_name: "deadline".to_string(),
153+
value: Value::Literal(LiteralValue {
154+
value: json!("2025-12-31"),
155+
value_type: "date".to_string(),
156+
}),
157+
}],
158+
is_strict: false,
157159
},
158-
]);
159-
160-
vec![match_clause, fetch_clause]
160+
])
161161
}
162162

163163
fn make_batch_clauses() -> Vec<Clause> {
@@ -268,10 +268,10 @@ fn bench_compile_match_with_constraints(c: &mut Criterion) {
268268

269269
fn bench_compile_complex_query(c: &mut Criterion) {
270270
let compiler = QueryCompiler::new();
271-
let clauses = make_complex_query();
271+
let clause = make_complex_query();
272272

273273
c.bench_function("compile/complex_10_patterns", |b| {
274-
b.iter(|| compiler.compile(black_box(&clauses)))
274+
b.iter(|| compiler.compile_clause(black_box(&clause)))
275275
});
276276
}
277277

@@ -305,12 +305,33 @@ fn bench_compile_reduce(c: &mut Criterion) {
305305
}),
306306
},
307307
ReduceAssignment {
308-
variable: "$total".to_string(),
308+
variable: "$total_salary".to_string(),
309309
expression: Value::FunctionCall(FunctionCallValue {
310310
function: "sum".to_string(),
311311
args: vec![Value::Variable("$salary".to_string())],
312312
}),
313313
},
314+
ReduceAssignment {
315+
variable: "$max_age".to_string(),
316+
expression: Value::FunctionCall(FunctionCallValue {
317+
function: "max".to_string(),
318+
args: vec![Value::Variable("$age".to_string())],
319+
}),
320+
},
321+
ReduceAssignment {
322+
variable: "$min_age".to_string(),
323+
expression: Value::FunctionCall(FunctionCallValue {
324+
function: "min".to_string(),
325+
args: vec![Value::Variable("$age".to_string())],
326+
}),
327+
},
328+
ReduceAssignment {
329+
variable: "$avg_score".to_string(),
330+
expression: Value::FunctionCall(FunctionCallValue {
331+
function: "mean".to_string(),
332+
args: vec![Value::Variable("$score".to_string())],
333+
}),
334+
},
314335
],
315336
group_by: Some("$dept".to_string()),
316337
};
@@ -991,6 +1012,18 @@ fn bench_compile_isa_constraint(c: &mut Criterion) {
9911012
],
9921013
is_strict: false,
9931014
},
1015+
Pattern::Entity {
1016+
variable: "$v".to_string(),
1017+
type_name: "vehicle".to_string(),
1018+
constraints: vec![
1019+
Constraint::Isa { type_name: "electric-vehicle".to_string(), strict: false },
1020+
Constraint::Has {
1021+
attr_name: "range".to_string(),
1022+
value: Value::Literal(LiteralValue { value: json!(300), value_type: "long".to_string() }),
1023+
},
1024+
],
1025+
is_strict: false,
1026+
},
9941027
]);
9951028
c.bench_function("compile/isa_constraint", |b| {
9961029
b.iter(|| compiler.compile_clause(black_box(&clause)))

type-bridge-core/crates/core/benches/validation.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,19 @@ fn bench_validate_reserved_word(c: &mut Criterion) {
3838

3939
fn bench_validate_batch_names(c: &mut Criterion) {
4040
let engine = ValidationEngine::new();
41-
let names: Vec<String> = (0..1000)
42-
.map(|i| format!("type-name-{}", i))
43-
.collect();
41+
let mut names: Vec<String> = Vec::with_capacity(1000);
42+
for i in 0..400 {
43+
names.push(format!("entity-type-{}", i));
44+
}
45+
for i in 0..300 {
46+
names.push(format!("my-long-attribute-name-for-testing-{}", i));
47+
}
48+
for i in 0..200 {
49+
names.push(format!("relation-{}-data", i));
50+
}
51+
for i in 0..100 {
52+
names.push(format!("\u{00e9}l\u{00e8}ve-{}", i));
53+
}
4454

4555
c.bench_function("validate_type_name/batch_1000", |b| {
4656
b.iter(|| {
@@ -240,9 +250,19 @@ fn bench_validate_statement_relation(c: &mut Criterion) {
240250

241251
fn bench_validate_batch_5000(c: &mut Criterion) {
242252
let engine = ValidationEngine::new();
243-
let names: Vec<String> = (0..5000)
244-
.map(|i| format!("type-name-{}", i))
245-
.collect();
253+
let mut names: Vec<String> = Vec::with_capacity(5000);
254+
for i in 0..2000 {
255+
names.push(format!("entity-type-{}", i));
256+
}
257+
for i in 0..1500 {
258+
names.push(format!("my-long-attribute-name-for-testing-{}", i));
259+
}
260+
for i in 0..1000 {
261+
names.push(format!("relation-{}-data", i));
262+
}
263+
for i in 0..500 {
264+
names.push(format!("\u{00e9}l\u{00e8}ve-{}", i));
265+
}
246266

247267
c.bench_function("validate_type_name/batch_5000", |b| {
248268
b.iter(|| {

0 commit comments

Comments
 (0)