Skip to content

Commit 87a3563

Browse files
committed
fix rust
1 parent 736ad9e commit 87a3563

File tree

13 files changed

+687
-554
lines changed

13 files changed

+687
-554
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@ Servers need to include converters between all versions.
7171

7272
Clients only need to inlucde a single version of the schema since the server is responsible for version conversion no matter what version you connect with.
7373

74-
**Embedded vs Negotiated Versions**
74+
**Embedded vs Pre-Negotiated Versions**
7575

7676
Every message has a version associated with it. This version is either:
7777

78-
- Pre-negotiated (via mechanisms like HTTP request query parameters or handshakes)
79-
- For example, you can extract the version from a request like `POST /v3/users`
80-
- Embedded in the message itself in the first 2 bytes of the message (see below)
78+
- **Embedded** in the message itself in the first 2 bytes of the message (see below)
79+
- **Pre-negotiated** via mechanisms like HTTP request query parameters or handshakes
80+
- For example, you can extract the version from a request like `POST /v3/users` as `v3`
8181

8282
**Embedded Binary Format**
8383

@@ -96,10 +96,10 @@ The layout looks like this:
9696

9797
The core of why VBARE was designed this way is:
9898

99-
- Manual evolutions simplify application logic by putting all complex evolutions & defaults in a conversion code instead of inside your core applciation logic
100-
- Manual evolution forces you to handle edge cases of migrations & breaking changes at the cost of more verbose migration code
101-
- Stop making big breaking v1 to v2 changes — instead, make much smaller schema changes with more flexibility
102-
- Schema evolution frequently requires more than just renaming properties (like Protobuf, Flatbuffers, Cap'n'proto) — more complicated reshaping & fetching data from remote sources is commonly needed
99+
- Manual evolutions **simplifies application logic** by putting all complex evolutions & defaults in a conversion code instead of inside your core application logic
100+
- Manual evolution **forces developers to handle edge cases** of migrations & breaking changes at the cost of more verbose migration code
101+
- Stop making big breaking v1 to v2 changes — instead, **make much smaller schema changes** with more flexibility
102+
- Schema evolution frequently requires more than just renaming properties (like Protobuf, Flatbuffers, Cap'n'proto) — **more complicated reshaping & fetching data** from remote sources is commonly needed
103103

104104
## Implementations
105105

rust/examples/basic/build.rs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::{env, fs, path::{Path, PathBuf}};
1+
use std::{
2+
env, fs,
3+
path::{Path, PathBuf},
4+
};
25

36
use anyhow::Result;
47

@@ -10,7 +13,7 @@ fn main() -> Result<()> {
1013
repo_root.pop(); // rust/examples/basic -> rust/examples
1114
repo_root.pop(); // rust/examples -> rust
1215
repo_root.pop(); // rust -> repo root
13-
// Now at repo root
16+
// Now at repo root
1417

1518
let fixtures_dir = repo_root.join("fixtures/tests/basic");
1619
println!("cargo:rerun-if-changed={}", fixtures_dir.display());
@@ -34,7 +37,10 @@ fn main() -> Result<()> {
3437
// - map<K, V> -> map<K><V>
3538
let mut normalized = String::new();
3639
for line in content_raw.lines() {
37-
let line_wo_comment = match line.find("//") { Some(i) => &line[..i], None => line };
40+
let line_wo_comment = match line.find("//") {
41+
Some(i) => &line[..i],
42+
None => line,
43+
};
3844
let trimmed = line_wo_comment.trim_start();
3945
let converted = if trimmed.starts_with("enum ") {
4046
let rest = &trimmed["enum ".len()..];
@@ -65,17 +71,29 @@ fn main() -> Result<()> {
6571
// Ensure ChangeKind enum is defined before Change struct
6672
let mut lines_all: Vec<&str> = normalized.lines().collect();
6773

68-
fn extract_block<'a>(lines: &mut Vec<&'a str>, start_pred: &str) -> Option<Vec<&'a str>> {
69-
let start = lines.iter().position(|l| l.trim_start().starts_with(start_pred))?;
74+
fn extract_block<'a>(
75+
lines: &mut Vec<&'a str>,
76+
start_pred: &str,
77+
) -> Option<Vec<&'a str>> {
78+
let start = lines
79+
.iter()
80+
.position(|l| l.trim_start().starts_with(start_pred))?;
7081
let mut end = start;
7182
let mut brace_count = 0i32;
7283
let mut seen_open = false;
7384
for i in start..lines.len() {
7485
let l = lines[i];
75-
if l.contains('{') { brace_count += 1; seen_open = true; }
76-
if l.contains('}') { brace_count -= 1; }
86+
if l.contains('{') {
87+
brace_count += 1;
88+
seen_open = true;
89+
}
90+
if l.contains('}') {
91+
brace_count -= 1;
92+
}
7793
end = i;
78-
if seen_open && brace_count == 0 { break; }
94+
if seen_open && brace_count == 0 {
95+
break;
96+
}
7997
}
8098
let block: Vec<&str> = lines[start..=end].to_vec();
8199
lines.drain(start..=end);
@@ -92,8 +110,12 @@ fn main() -> Result<()> {
92110
.unwrap_or(lines_all.len());
93111
let mut rebuilt: Vec<&str> = Vec::new();
94112
rebuilt.extend_from_slice(&lines_all[..insert_at]);
95-
for l in kind_block.unwrap() { rebuilt.push(l); }
96-
for l in change_block.unwrap() { rebuilt.push(l); }
113+
for l in kind_block.unwrap() {
114+
rebuilt.push(l);
115+
}
116+
for l in change_block.unwrap() {
117+
rebuilt.push(l);
118+
}
97119
rebuilt.extend_from_slice(&lines_all[insert_at..]);
98120
normalized = rebuilt.join("\n");
99121
}
@@ -107,7 +129,9 @@ fn main() -> Result<()> {
107129
let mut all_names = Vec::new();
108130
for entry in fs::read_dir(&schema_dir)?.flatten() {
109131
let path = entry.path();
110-
if path.is_dir() { continue; }
132+
if path.is_dir() {
133+
continue;
134+
}
111135
let bare_name = path
112136
.file_name()
113137
.and_then(|s| s.to_str())
@@ -116,7 +140,12 @@ fn main() -> Result<()> {
116140
.expect("valid file name");
117141

118142
// Use HashMap instead of rivet_util::HashableMap to avoid extra dependency here.
119-
let tokens = vbare_gen::bare_schema(&path, vbare_gen::Config { use_hashable_map: false });
143+
let tokens = vbare_gen::bare_schema(
144+
&path,
145+
vbare_gen::Config {
146+
use_hashable_map: false,
147+
},
148+
);
120149
let ast = syn::parse2(tokens).expect("parse generated code");
121150
let content = prettyplease::unparse(&ast);
122151
fs::write(out_path.join(format!("{bare_name}_generated.rs")), content)?;

rust/examples/basic/tests/migrator.rs

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,16 @@ use vbare::OwnedVersionedData;
77
fn migrates_v1_to_v3() {
88
let app_v1 = schemas::v1::App {
99
todos: vec![
10-
schemas::v1::Todo { id: 1, title: "a".into(), done: false },
11-
schemas::v1::Todo { id: 2, title: "b".into(), done: true },
10+
schemas::v1::Todo {
11+
id: 1,
12+
title: "a".into(),
13+
done: false,
14+
},
15+
schemas::v1::Todo {
16+
id: 2,
17+
title: "b".into(),
18+
done: true,
19+
},
1220
],
1321
};
1422

@@ -23,14 +31,20 @@ fn migrates_v1_to_v3() {
2331
#[test]
2432
fn migrates_v2_to_v3_with_tags() {
2533
let mut todos: HashMap<schemas::v2::TodoId, schemas::v2::Todo> = HashMap::new();
26-
todos.insert(5, schemas::v2::Todo {
27-
id: 5,
28-
title: "with-tags".into(),
29-
status: schemas::v2::TodoStatus::Open,
30-
created_at: 42,
31-
tags: vec!["red".into(), "blue".into()],
32-
});
33-
let app_v2 = schemas::v2::App { todos, settings: HashMap::new() };
34+
todos.insert(
35+
5,
36+
schemas::v2::Todo {
37+
id: 5,
38+
title: "with-tags".into(),
39+
status: schemas::v2::TodoStatus::Open,
40+
created_at: 42,
41+
tags: vec!["red".into(), "blue".into()],
42+
},
43+
);
44+
let app_v2 = schemas::v2::App {
45+
todos,
46+
settings: HashMap::new(),
47+
};
3448
let bytes = serde_bare::to_vec(&app_v2).unwrap();
3549
let migrated = AppVersioned::deserialize(&bytes, 2).unwrap();
3650

@@ -56,14 +70,20 @@ fn serializes_v3_to_v1() {
5670
user_id: None,
5771
team_id: None,
5872
},
59-
detail: schemas::v3::TodoDetail { title: "hello".into(), tags: HashMap::new() },
73+
detail: schemas::v3::TodoDetail {
74+
title: "hello".into(),
75+
tags: HashMap::new(),
76+
},
6077
history: Vec::new(),
6178
},
6279
);
6380

6481
let app_v3 = schemas::v3::App {
6582
todos,
66-
config: schemas::v3::AppConfig { theme: schemas::v3::Theme::System, features: HashMap::new() },
83+
config: schemas::v3::AppConfig {
84+
theme: schemas::v3::Theme::System,
85+
features: HashMap::new(),
86+
},
6787
boards: HashMap::new(),
6888
};
6989

rust/vbare-compiler/src/lib.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,46 @@
1-
use std::{fs, path::Path};
21
use indoc::formatdoc;
2+
use std::{fs, path::Path};
3+
4+
/// Configuration for the vbare-compiler.
5+
///
6+
/// This allows callers to control how code generation behaves, including
7+
/// passing through configuration to `vbare_gen`.
8+
pub struct Config {
9+
/// Configuration forwarded to `vbare_gen` for schema codegen.
10+
pub vbare: vbare_gen::Config,
11+
}
12+
13+
impl Default for Config {
14+
fn default() -> Self {
15+
Self {
16+
vbare: vbare_gen::Config {
17+
use_hashable_map: false,
18+
},
19+
}
20+
}
21+
}
22+
23+
impl Config {
24+
/// Convenience helper to enable hashable maps in generated code.
25+
pub fn with_hashable_map() -> Self {
26+
Self {
27+
vbare: vbare_gen::Config {
28+
use_hashable_map: true,
29+
},
30+
}
31+
}
32+
}
333

434
/// Process BARE schema files and generate Rust code.
535
pub fn process_schemas(schema_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
36+
process_schemas_with_config(schema_dir, &Config::default())
37+
}
38+
39+
/// Process BARE schema files and generate Rust code, using the provided config.
40+
pub fn process_schemas_with_config(
41+
schema_dir: &Path,
42+
config: &Config,
43+
) -> Result<(), Box<dyn std::error::Error>> {
644
let out_dir = std::env::var("OUT_DIR")?;
745
let out_path = Path::new(&out_dir);
846

@@ -26,7 +64,12 @@ pub fn process_schemas(schema_dir: &Path) -> Result<(), Box<dyn std::error::Erro
2664
.ok_or("No file extension")?
2765
.0;
2866

29-
let tokens = vbare_gen::bare_schema(&path, vbare_gen::Config { use_hashable_map: true });
67+
let tokens = vbare_gen::bare_schema(
68+
&path,
69+
vbare_gen::Config {
70+
use_hashable_map: config.vbare.use_hashable_map,
71+
},
72+
);
3073
let ast = syn::parse2(tokens)?;
3174
let content = prettyplease::unparse(&ast);
3275

rust/vbare-compiler/tests/basic.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ fn processes_basic_fixtures() {
99
fixtures_dir.pop(); // up to repo root
1010
fixtures_dir.push("fixtures/tests/basic");
1111

12-
assert!(fixtures_dir.is_dir(), "fixtures dir missing: {}", fixtures_dir.display());
12+
assert!(
13+
fixtures_dir.is_dir(),
14+
"fixtures dir missing: {}",
15+
fixtures_dir.display()
16+
);
1317

1418
// Create a dedicated OUT_DIR for the test and set env var
1519
let out_dir = tempfile::tempdir().expect("create tempdir for OUT_DIR");
@@ -32,7 +36,10 @@ fn processes_basic_fixtures() {
3236
// - map<K, V> -> map<K><V>
3337
let mut normalized = String::new();
3438
for line in content_raw.lines() {
35-
let line_wo_comment = match line.find("//") { Some(i) => &line[..i], None => line };
39+
let line_wo_comment = match line.find("//") {
40+
Some(i) => &line[..i],
41+
None => line,
42+
};
3643
let trimmed = line_wo_comment.trim_start();
3744
let converted = if trimmed.starts_with("enum ") {
3845
let rest = &trimmed["enum ".len()..];
@@ -65,17 +72,29 @@ fn processes_basic_fixtures() {
6572
// Reorder so that ChangeKind enum appears before Change struct, and both before Todo
6673
let mut lines_all: Vec<&str> = normalized.lines().collect();
6774

68-
fn extract_block<'a>(lines: &mut Vec<&'a str>, start_pred: &str) -> Option<Vec<&'a str>> {
69-
let start = lines.iter().position(|l| l.trim_start().starts_with(start_pred))?;
75+
fn extract_block<'a>(
76+
lines: &mut Vec<&'a str>,
77+
start_pred: &str,
78+
) -> Option<Vec<&'a str>> {
79+
let start = lines
80+
.iter()
81+
.position(|l| l.trim_start().starts_with(start_pred))?;
7082
let mut end = start;
7183
let mut brace_count = 0i32;
7284
let mut seen_open = false;
7385
for i in start..lines.len() {
7486
let l = lines[i];
75-
if l.contains('{') { brace_count += 1; seen_open = true; }
76-
if l.contains('}') { brace_count -= 1; }
87+
if l.contains('{') {
88+
brace_count += 1;
89+
seen_open = true;
90+
}
91+
if l.contains('}') {
92+
brace_count -= 1;
93+
}
7794
end = i;
78-
if seen_open && brace_count == 0 { break; }
95+
if seen_open && brace_count == 0 {
96+
break;
97+
}
7998
}
8099
let block: Vec<&str> = lines[start..=end].to_vec();
81100
lines.drain(start..=end);
@@ -92,8 +111,12 @@ fn processes_basic_fixtures() {
92111
.unwrap_or(lines_all.len());
93112
let mut rebuilt: Vec<&str> = Vec::new();
94113
rebuilt.extend_from_slice(&lines_all[..insert_at]);
95-
for l in kind_block.unwrap() { rebuilt.push(l); }
96-
for l in change_block.unwrap() { rebuilt.push(l); }
114+
for l in kind_block.unwrap() {
115+
rebuilt.push(l);
116+
}
117+
for l in change_block.unwrap() {
118+
rebuilt.push(l);
119+
}
97120
rebuilt.extend_from_slice(&lines_all[insert_at..]);
98121
normalized = rebuilt.join("\n");
99122
}

rust/vbare-gen/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ impl SchemaGenerator {
264264
let fields_gen = self.gen_struct_field(name, fields_clone);
265265
self.gen_anonymous(name, |ident| {
266266
quote! {
267-
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
267+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash)]
268268
pub struct #ident {
269269
#(#fields_gen),*
270270
}
@@ -317,7 +317,7 @@ impl SchemaGenerator {
317317
}
318318
self.gen_anonymous(name, |ident| {
319319
quote! {
320-
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
320+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash)]
321321
pub enum #ident {
322322
#(#members_def),*
323323
}
@@ -365,7 +365,7 @@ impl SchemaGenerator {
365365
});
366366
self.gen_anonymous(name, |ident| {
367367
quote! {
368-
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, PartialOrd, Ord, Clone)]
368+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, PartialOrd, Ord, Hash, Clone)]
369369
#[repr(usize)]
370370
pub enum #ident {
371371
#(#member_defs),*

0 commit comments

Comments
 (0)