Initial commit: Rhai model builder with code generation
This commit is contained in:
commit
75f238cb4d
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
|
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
/src/gen/
|
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "rhai_meeting_builder"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rhai = "1"
|
||||||
|
quote = "1"
|
||||||
|
syn = { version = "2", features = ["full"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
syn = { version = "2", features = ["full"] }
|
||||||
|
quote = "1"
|
||||||
|
proc-macro2 = "1"
|
83
README.md
Normal file
83
README.md
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# Generated Model Code
|
||||||
|
|
||||||
|
This README documents the automatically generated code for the data models.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Meeting](#meeting)
|
||||||
|
- [Task](#task)
|
||||||
|
|
||||||
|
## Meeting
|
||||||
|
|
||||||
|
### Fields
|
||||||
|
- `id`: String
|
||||||
|
- `title`: String
|
||||||
|
- `description`: String
|
||||||
|
- `participants`: Vec<String>
|
||||||
|
- `start`: String
|
||||||
|
|
||||||
|
### Builder Methods
|
||||||
|
- `with_title(value)`: Sets the title field (String)
|
||||||
|
- `with_description(value)`: Sets the description field (String)
|
||||||
|
- `with_participants(value)`: Sets the participants field (Vec<String>)
|
||||||
|
- `with_start(value)`: Sets the start field (String)
|
||||||
|
- `meeting()`: Creates a new Meeting builder
|
||||||
|
- `get_fields()`: Returns a Map of all set fields
|
||||||
|
|
||||||
|
### Database Operations
|
||||||
|
- `create_meeting_typed(fields)`: Creates a new Meeting in the database
|
||||||
|
- `find_meeting_by_id_typed(id)`: Finds a Meeting by ID
|
||||||
|
- `update_meeting_typed(id, fields)`: Updates a Meeting in the database
|
||||||
|
- `delete_meeting_typed(id)`: Deletes a Meeting from the database
|
||||||
|
- `list_meetings_typed()`: Lists all Meetings in the database
|
||||||
|
- `filter_meetings_typed(query)`: Filters Meetings based on query parameters
|
||||||
|
- `find_meeting_typed(query)`: Finds a specific Meeting based on query parameters
|
||||||
|
|
||||||
|
### Rhai Integration
|
||||||
|
- `create_meeting(fields)`: Creates a new Meeting in the database
|
||||||
|
- `find_meeting_by_id(id)`: Finds a Meeting by ID
|
||||||
|
- `update_meeting(id, fields)`: Updates a Meeting in the database
|
||||||
|
- `delete_meeting(id)`: Deletes a Meeting from the database
|
||||||
|
- `list_meetings()`: Lists all Meetings in the database
|
||||||
|
- `filter_meetings(query)`: Filters Meetings based on query parameters
|
||||||
|
- `find_meeting(query)`: Finds a specific Meeting based on query parameters
|
||||||
|
|
||||||
|
## Task
|
||||||
|
|
||||||
|
### Fields
|
||||||
|
- `id`: String
|
||||||
|
- `title`: String
|
||||||
|
- `description`: String
|
||||||
|
- `assignee`: String
|
||||||
|
- `due_date`: String
|
||||||
|
- `priority`: String
|
||||||
|
- `completed`: bool
|
||||||
|
|
||||||
|
### Builder Methods
|
||||||
|
- `with_title(value)`: Sets the title field (String)
|
||||||
|
- `with_description(value)`: Sets the description field (String)
|
||||||
|
- `with_assignee(value)`: Sets the assignee field (String)
|
||||||
|
- `with_due_date(value)`: Sets the due_date field (String)
|
||||||
|
- `with_priority(value)`: Sets the priority field (String)
|
||||||
|
- `with_completed(value)`: Sets the completed field (bool)
|
||||||
|
- `task()`: Creates a new Task builder
|
||||||
|
- `get_fields()`: Returns a Map of all set fields
|
||||||
|
|
||||||
|
### Database Operations
|
||||||
|
- `create_task_typed(fields)`: Creates a new Task in the database
|
||||||
|
- `find_task_by_id_typed(id)`: Finds a Task by ID
|
||||||
|
- `update_task_typed(id, fields)`: Updates a Task in the database
|
||||||
|
- `delete_task_typed(id)`: Deletes a Task from the database
|
||||||
|
- `list_tasks_typed()`: Lists all Tasks in the database
|
||||||
|
- `filter_tasks_typed(query)`: Filters Tasks based on query parameters
|
||||||
|
- `find_task_typed(query)`: Finds a specific Task based on query parameters
|
||||||
|
|
||||||
|
### Rhai Integration
|
||||||
|
- `create_task(fields)`: Creates a new Task in the database
|
||||||
|
- `find_task_by_id(id)`: Finds a Task by ID
|
||||||
|
- `update_task(id, fields)`: Updates a Task in the database
|
||||||
|
- `delete_task(id)`: Deletes a Task from the database
|
||||||
|
- `list_tasks()`: Lists all Tasks in the database
|
||||||
|
- `filter_tasks(query)`: Filters Tasks based on query parameters
|
||||||
|
- `find_task(query)`: Finds a specific Task based on query parameters
|
||||||
|
|
199
specs.md
Normal file
199
specs.md
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
Here's an updated full project specification including:
|
||||||
|
1. ✅ Unified builder for both creation and filtering
|
||||||
|
2. ✅ with_...() methods used for both creation and filtering
|
||||||
|
3. ✅ Auto-generated CRUD + list + filter + find methods
|
||||||
|
4. ✅ Database interface for persistence operations
|
||||||
|
5. ✅ Auto-generated registration code
|
||||||
|
6. ✅ 📜 Example Rhai script
|
||||||
|
7. ✅ 🔍 build.rs should output to src/gen/meeting_builder.rs
|
||||||
|
|
||||||
|
⸻
|
||||||
|
|
||||||
|
🧾 Project Spec: Rust → Rhai Builder Code Generator
|
||||||
|
|
||||||
|
🎯 Goal
|
||||||
|
|
||||||
|
Build a build.rs script that parses Rust structs annotated with #[builder] and generates Rhai-compatible unified builders with with_...() methods that populate a single field map used for both creation and filtering. Also generate CRUD operations that use a database interface.
|
||||||
|
|
||||||
|
⸻
|
||||||
|
|
||||||
|
📥 Input Example: src/models.rs
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Meeting {
|
||||||
|
pub id: String,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub participants: Vec<String>,
|
||||||
|
pub start: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
⸻
|
||||||
|
|
||||||
|
📤 Generated Code: src/gen/meeting_builder.rs
|
||||||
|
|
||||||
|
✅ 1. MeetingBuilder Struct
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MeetingBuilder {
|
||||||
|
pub fields: std::collections::HashMap<String, rhai::Dynamic>,
|
||||||
|
}
|
||||||
|
|
||||||
|
✅ 2. with_...() Methods (for each field)
|
||||||
|
|
||||||
|
impl MeetingBuilder {
|
||||||
|
pub fn with_title(mut self, value: &str) -> Self {
|
||||||
|
self.fields.insert("title".into(), value.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_participants(mut self, value: Vec<rhai::Dynamic>) -> Self {
|
||||||
|
self.fields.insert("participants".into(), value.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fields(&mut self) -> rhai::Map {
|
||||||
|
self.fields.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRUD operations
|
||||||
|
pub fn create(&mut self, db: &dyn Database) -> rhai::Dynamic {
|
||||||
|
db.create("meetings", self.get_fields())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_by_id(&mut self, db: &dyn Database, id: &str) -> rhai::Dynamic {
|
||||||
|
db.find_by_id("meetings", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, db: &dyn Database, id: &str) -> rhai::Dynamic {
|
||||||
|
db.update("meetings", id, self.get_fields())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete(&mut self, db: &dyn Database, id: &str) -> rhai::Dynamic {
|
||||||
|
db.delete("meetings", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list(&mut self, db: &dyn Database) -> rhai::Dynamic {
|
||||||
|
db.list("meetings")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn filter(&mut self, db: &dyn Database) -> rhai::Dynamic {
|
||||||
|
db.filter("meetings", self.get_fields())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find(&mut self, db: &dyn Database) -> rhai::Dynamic {
|
||||||
|
db.find("meetings", self.get_fields())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
✅ 3. Database Interface
|
||||||
|
|
||||||
|
pub trait Database {
|
||||||
|
fn create(&self, collection: &str, fields: rhai::Map) -> rhai::Dynamic;
|
||||||
|
fn find_by_id(&self, collection: &str, id: &str) -> rhai::Dynamic;
|
||||||
|
fn update(&self, collection: &str, id: &str, fields: rhai::Map) -> rhai::Dynamic;
|
||||||
|
fn delete(&self, collection: &str, id: &str) -> rhai::Dynamic;
|
||||||
|
fn list(&self, collection: &str) -> rhai::Dynamic;
|
||||||
|
fn filter(&self, collection: &str, query: rhai::Map) -> rhai::Dynamic;
|
||||||
|
fn find(&self, collection: &str, query: rhai::Map) -> rhai::Dynamic;
|
||||||
|
}
|
||||||
|
|
||||||
|
✅ 4. Rhai Registration
|
||||||
|
|
||||||
|
pub fn register_meeting_module(engine: &mut rhai::Engine) {
|
||||||
|
engine.register_type::<MeetingBuilder>();
|
||||||
|
engine.register_fn("meeting", MeetingBuilder::new);
|
||||||
|
|
||||||
|
engine.register_fn("with_title", MeetingBuilder::with_title);
|
||||||
|
engine.register_fn("with_participants", MeetingBuilder::with_participants);
|
||||||
|
engine.register_fn("get_fields", MeetingBuilder::get_fields);
|
||||||
|
|
||||||
|
// Register CRUD operations
|
||||||
|
engine.register_fn("create", MeetingBuilder::create);
|
||||||
|
engine.register_fn("find_by_id", MeetingBuilder::find_by_id);
|
||||||
|
engine.register_fn("update", MeetingBuilder::update);
|
||||||
|
engine.register_fn("delete", MeetingBuilder::delete);
|
||||||
|
engine.register_fn("list", MeetingBuilder::list);
|
||||||
|
engine.register_fn("filter", MeetingBuilder::filter);
|
||||||
|
engine.register_fn("find", MeetingBuilder::find);
|
||||||
|
}
|
||||||
|
|
||||||
|
⸻
|
||||||
|
|
||||||
|
✅ Example Rhai Script
|
||||||
|
|
||||||
|
// Create a meeting
|
||||||
|
let meeting_id = meeting()
|
||||||
|
.with_title("Project Sync")
|
||||||
|
.with_description("Check in with the team")
|
||||||
|
.with_participants(["timur", "eda"])
|
||||||
|
.with_start("2024-06-01T10:00")
|
||||||
|
.create(db);
|
||||||
|
|
||||||
|
// Find a meeting by ID
|
||||||
|
let meeting = meeting().find_by_id(db, meeting_id);
|
||||||
|
|
||||||
|
// Update a meeting
|
||||||
|
meeting()
|
||||||
|
.with_description("Updated description")
|
||||||
|
.update(db, meeting_id);
|
||||||
|
|
||||||
|
// Delete a meeting
|
||||||
|
meeting().delete(db, meeting_id);
|
||||||
|
|
||||||
|
// List all meetings
|
||||||
|
let all_meetings = meeting().list(db);
|
||||||
|
|
||||||
|
// Filter meetings by description
|
||||||
|
let filtered_meetings = meeting()
|
||||||
|
.with_description("Project Sync")
|
||||||
|
.filter(db);
|
||||||
|
|
||||||
|
// Find a specific meeting
|
||||||
|
let found_meeting = meeting()
|
||||||
|
.with_title("Project Sync")
|
||||||
|
.with_start("2024-06-01T10:00")
|
||||||
|
.find(db);
|
||||||
|
|
||||||
|
This script uses the same builder for all operations.
|
||||||
|
|
||||||
|
⸻
|
||||||
|
|
||||||
|
📂 Output File
|
||||||
|
|
||||||
|
Write generated code to:
|
||||||
|
|
||||||
|
src/gen/meeting_builder.rs
|
||||||
|
|
||||||
|
And include it in main.rs:
|
||||||
|
|
||||||
|
#[path = "gen/meeting_builder.rs"]
|
||||||
|
mod meeting_builder;
|
||||||
|
|
||||||
|
⸻
|
||||||
|
|
||||||
|
🔨 build.rs Responsibilities
|
||||||
|
• Use syn to parse src/models.rs
|
||||||
|
• Extract structs and fields
|
||||||
|
• Generate:
|
||||||
|
• MeetingBuilder struct with fields map
|
||||||
|
• All with_...() methods for Rhai
|
||||||
|
• get_fields() method
|
||||||
|
• CRUD + list + filter + find methods
|
||||||
|
• Database trait
|
||||||
|
• register_meeting_module(engine: &mut Engine) function
|
||||||
|
|
||||||
|
Output to src/gen/meeting_builder.rs.
|
||||||
|
|
||||||
|
⸻
|
||||||
|
|
||||||
|
✅ Success Criteria
|
||||||
|
• No duplication between filter/create logic
|
||||||
|
• Fluent API in Rhai
|
||||||
|
• Generated code compiles and integrates
|
||||||
|
• build.rs works as part of standard Rust build pipeline
|
||||||
|
• Easy to extend to more models (e.g., User, Task)
|
||||||
|
• Complete CRUD operations available
|
||||||
|
• Database abstraction for persistence
|
||||||
|
|
||||||
|
⸻
|
123
src/main.rs
Normal file
123
src/main.rs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
mod models;
|
||||||
|
#[path = "gen/meeting_builder.rs"]
|
||||||
|
pub mod meeting_builder;
|
||||||
|
#[path = "gen/meeting_db.rs"]
|
||||||
|
pub mod meeting_db;
|
||||||
|
#[path = "gen/meeting_rhai.rs"]
|
||||||
|
pub mod meeting_rhai;
|
||||||
|
#[path = "gen/task_builder.rs"]
|
||||||
|
pub mod task_builder;
|
||||||
|
#[path = "gen/task_db.rs"]
|
||||||
|
pub mod task_db;
|
||||||
|
#[path = "gen/task_rhai.rs"]
|
||||||
|
pub mod task_rhai;
|
||||||
|
#[path = "gen/tests/mod.rs"]
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use rhai::{Engine, Scope};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Dynamic code generation example with CRUD operations");
|
||||||
|
|
||||||
|
// Create a new Rhai engine
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
// Register the println function
|
||||||
|
engine.register_fn("println", |s: &str| println!("{}", s));
|
||||||
|
|
||||||
|
// Register the meeting builder module
|
||||||
|
meeting_builder::register_meeting_module(&mut engine);
|
||||||
|
|
||||||
|
// Register the meeting database module
|
||||||
|
meeting_rhai::register_meeting_db_module(&mut engine);
|
||||||
|
|
||||||
|
// Register the task builder module
|
||||||
|
task_builder::register_task_module(&mut engine);
|
||||||
|
|
||||||
|
// Register the task database module
|
||||||
|
task_rhai::register_task_db_module(&mut engine);
|
||||||
|
|
||||||
|
// Create a scope
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
// Define the Rhai script that uses CRUD operations for both models
|
||||||
|
let script = r#"
|
||||||
|
// ===== Meeting CRUD operations =====
|
||||||
|
println("=== Meeting CRUD Operations ===");
|
||||||
|
|
||||||
|
// Create a meeting
|
||||||
|
let builder = meeting()
|
||||||
|
.with_title("Project Sync")
|
||||||
|
.with_description("Check in with the team")
|
||||||
|
.with_participants(["timur", "eda"])
|
||||||
|
.with_start("2024-06-01T10:00");
|
||||||
|
let meeting_id = create_meeting(builder.get_fields());
|
||||||
|
|
||||||
|
print(`Created meeting with ID: ${meeting_id}`);
|
||||||
|
|
||||||
|
// Find a meeting by ID
|
||||||
|
let meeting = find_meeting_by_id(meeting_id);
|
||||||
|
print(`Found meeting: ${meeting}`);
|
||||||
|
|
||||||
|
// Update a meeting
|
||||||
|
let update_builder = meeting()
|
||||||
|
.with_description("Updated meeting description");
|
||||||
|
let updated = update_meeting(meeting_id, update_builder.get_fields());
|
||||||
|
print(`Update result: ${updated}`);
|
||||||
|
|
||||||
|
// List all meetings
|
||||||
|
let all_meetings = list_meetings();
|
||||||
|
print(`All meetings: ${all_meetings}`);
|
||||||
|
|
||||||
|
// ===== Task CRUD operations =====
|
||||||
|
println("=== Task CRUD Operations ===");
|
||||||
|
|
||||||
|
// Create a task
|
||||||
|
let task_builder = task()
|
||||||
|
.with_title("Implement CRUD")
|
||||||
|
.with_description("Implement CRUD operations for all models")
|
||||||
|
.with_assignee("timur")
|
||||||
|
.with_due_date("2024-06-15")
|
||||||
|
.with_priority("high")
|
||||||
|
.with_completed(false);
|
||||||
|
let task_id = create_task(task_builder.get_fields());
|
||||||
|
|
||||||
|
print(`Created task with ID: ${task_id}`);
|
||||||
|
|
||||||
|
// Find a task by ID
|
||||||
|
let task = find_task_by_id(task_id);
|
||||||
|
print(`Found task: ${task}`);
|
||||||
|
|
||||||
|
// Update a task
|
||||||
|
let task_update_builder = task()
|
||||||
|
.with_description("Updated task description")
|
||||||
|
.with_completed(true);
|
||||||
|
let task_updated = update_task(task_id, task_update_builder.get_fields());
|
||||||
|
print(`Update task result: ${task_updated}`);
|
||||||
|
|
||||||
|
// List all tasks
|
||||||
|
let all_tasks = list_tasks();
|
||||||
|
print(`All tasks: ${all_tasks}`);
|
||||||
|
|
||||||
|
// Filter tasks by assignee
|
||||||
|
let filter_builder = task()
|
||||||
|
.with_assignee("timur");
|
||||||
|
let filtered_tasks = filter_tasks(filter_builder.get_fields());
|
||||||
|
print(`Filtered tasks by assignee: ${filtered_tasks}`);
|
||||||
|
|
||||||
|
// Delete a task
|
||||||
|
let task_deleted = delete_task(task_id);
|
||||||
|
print(`Delete task result: ${task_deleted}`);
|
||||||
|
|
||||||
|
// Verify deletion by listing all tasks
|
||||||
|
let remaining_tasks = list_tasks();
|
||||||
|
print(`Remaining tasks: ${remaining_tasks}`);
|
||||||
|
"#;
|
||||||
|
|
||||||
|
// Run the script
|
||||||
|
match engine.eval_with_scope::<rhai::Dynamic>(&mut scope, script) {
|
||||||
|
Ok(_) => println!("Script executed successfully"),
|
||||||
|
Err(e) => eprintln!("Script error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
19
src/models.rs
Normal file
19
src/models.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Meeting {
|
||||||
|
pub id: String,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub participants: Vec<String>,
|
||||||
|
pub start: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Task {
|
||||||
|
pub id: String,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub assignee: String,
|
||||||
|
pub due_date: String,
|
||||||
|
pub priority: String,
|
||||||
|
pub completed: bool,
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user