Kyle
Kyle10mo ago

Rust Macro: convex_model!

https://github.com/ragkit/convex-macros I've been using the Convex Rust client and found working with nested objects a bit cumbersome. I made a macro that creates native Rust structs and enums based on a Typescript schema definition. Then you can build it from convex::Value and serialize it to serde_json::Value automatically.
convex_model!(User {
_id: v.id("users"),
name: v.string(),
age: v.optional(v.int64()),
platform: v.union(
v.object({
platform: v.literal("google"),
verified: v.boolean(),
}),
v.object({
platform: v.literal("github"),
username: v.string(),
}),
),
});
convex_model!(User {
_id: v.id("users"),
name: v.string(),
age: v.optional(v.int64()),
platform: v.union(
v.object({
platform: v.literal("google"),
verified: v.boolean(),
}),
v.object({
platform: v.literal("github"),
username: v.string(),
}),
),
});
This generates pub struct User {} with various methods to convert from convex::Value and to serde_json::Value.
// This is the shape you might get from a convex function you've defined.
let user = User::from_convex_value(&Value::Object(btreemap! {
"_id".into() => Value::String("1234".into()),
"name".into() => Value::String("Alice".into()),
"age".into() => Value::Int64(42),
"platform".into() => Value::Object(btreemap! {
"platform".into() => Value::String("github".into()),
"username".into() => Value::String("alicecodes".into()),
}),
}))
.expect("it should parse");

assert_eq!("1234", user._id);
assert_eq!("alicecodes", user.platform.as_2().unwrap().username);
assert_eq!(
json!({
"_id": "1234",
"name": "Alice",
"age": 42,
"platform": {
"platform": "github",
"username": "alicecodes",
},
}),
json!(user),
);
// This is the shape you might get from a convex function you've defined.
let user = User::from_convex_value(&Value::Object(btreemap! {
"_id".into() => Value::String("1234".into()),
"name".into() => Value::String("Alice".into()),
"age".into() => Value::Int64(42),
"platform".into() => Value::Object(btreemap! {
"platform".into() => Value::String("github".into()),
"username".into() => Value::String("alicecodes".into()),
}),
}))
.expect("it should parse");

assert_eq!("1234", user._id);
assert_eq!("alicecodes", user.platform.as_2().unwrap().username);
assert_eq!(
json!({
"_id": "1234",
"name": "Alice",
"age": 42,
"platform": {
"platform": "github",
"username": "alicecodes",
},
}),
json!(user),
);
Some Features: - let user = User::from_convex_value(value)?; to parse a value from Convex client. - json!(user) to serialize as json. - Discriminated unions are automatically handled. - Helper functions for each union branch: user.platform.as_2()?.username.
GitHub
GitHub - ragkit/convex-macros: Macros to help make Convex in Rust nice
Macros to help make Convex in Rust nice. Contribute to ragkit/convex-macros development by creating an account on GitHub.
Value in convex - Rust
A value that can be passed as an argument or returned from Convex functions. They correspond to the supported Convex types.
9 Replies
lee
lee10mo ago
so cool! i love how it uses the typescript syntax for the validator language! related in case you're interested in macros: https://github.com/get-convex/convex-backend/commit/23a8d403b94a3a5dbd676bc1fed02dc91e749ff4
GitHub
Add serde support for ConvexValue to help with serializing syst...
… metadata (#22841) It's very tedious to manually write the conversions between our in-memory structs (like TableMetadata) and ConvexValue. This change uses Serde to automate some part...
Kyle
KyleOP10mo ago
Nice, i'll check it out
CodingWithJamal
CodingWithJamal10mo ago
This is cool, thanks for sharing. A bit ago i also made something to help with convex typing. https://github.com/ThatGuyJamal/convex-typegen It works for simple schema but i never added recursion to check for nested objects and arrays.
GitHub
GitHub - ThatGuyJamal/convex-typegen: A convexdb typegen for rust
A convexdb typegen for rust. Contribute to ThatGuyJamal/convex-typegen development by creating an account on GitHub.
Kyle
KyleOP10mo ago
That's a cool project! I'm definitely thinking about how to make the query/mutation/action part integrated with rust better too, will be taking a look at your examples
CodingWithJamal
CodingWithJamal10mo ago
Yeah my main challenge was getting type safety in the queryies. I think I will try to get deep recursion working but its still hard to get function arguments type safe because then you also have to check the function files at build time and compair them which is a pain lol
Kyle
KyleOP10mo ago
Yeah the nested stuff can be annoying, parsing it into an intermediate structure I didn't find too bad. Here's the code I have for that: - The final ConvexField struct - Parsing entry point - Main parsing logic
GitHub
convex-macros/src/model.rs at main · ragkit/convex-macros
Macros to help make Convex in Rust nice. Contribute to ragkit/convex-macros development by creating an account on GitHub.
CodingWithJamal
CodingWithJamal10mo ago
yeah your code looks good. where is your macro code? Ive never gotton into the macro development side of rust dev yet
Kyle
KyleOP10mo ago
You set proc-macro = true in Cargo.toml, and then export a proc-macro function in src/lib.rs. Not very familiar with macro development myself, but this workshop and youtube video had some good examples to follow: - proc-macro-workshop - Jon streaming implementing the workshop
GitHub
GitHub - jonhoo/proc-macro-workshop: Learn to write Rust procedural...
Learn to write Rust procedural macros  [Rust Latam conference, Montevideo Uruguay, March 2019] - jonhoo/proc-macro-workshop
Jon Gjengset
YouTube
Procedural Macros in Rust (part 1)
In this stream, we take a look at Rust's procedural macros. These are much more flexible than the declarative macros you get with the macro_rules! construct, and effectively allow you to write source-code transformations as a separate program. Take a look at https://doc.rust-lang.org/book/ch19-06-macros.html and https://doc.rust-lang.org/nightly...
GitHub
convex-macros/Cargo.toml at main · ragkit/convex-macros
Macros to help make Convex in Rust nice. Contribute to ragkit/convex-macros development by creating an account on GitHub.
GitHub
convex-macros/src/lib.rs at main · ragkit/convex-macros
Macros to help make Convex in Rust nice. Contribute to ragkit/convex-macros development by creating an account on GitHub.
CodingWithJamal
CodingWithJamal10mo ago
oh cool thanks

Did you find this page helpful?