Skip to main content

romance_core/addon/
tasks.rs

1use crate::addon::Addon;
2use anyhow::Result;
3use std::path::Path;
4
5pub struct TasksAddon;
6
7impl Addon for TasksAddon {
8    fn name(&self) -> &str {
9        "tasks"
10    }
11
12    fn check_prerequisites(&self, project_root: &Path) -> Result<()> {
13        super::check_romance_project(project_root)
14    }
15
16    fn is_already_installed(&self, project_root: &Path) -> bool {
17        project_root.join("backend/src/tasks.rs").exists()
18    }
19
20    fn install(&self, project_root: &Path) -> Result<()> {
21        install_tasks(project_root)
22    }
23
24    fn uninstall(&self, project_root: &Path) -> Result<()> {
25        use colored::Colorize;
26
27        println!("{}", "Uninstalling background tasks...".bold());
28
29        // Delete files
30        if super::remove_file_if_exists(&project_root.join("backend/src/tasks.rs"))? {
31            println!("  {} backend/src/tasks.rs", "delete".red());
32        }
33        if super::remove_file_if_exists(&project_root.join("backend/src/scheduler.rs"))? {
34            println!("  {} backend/src/scheduler.rs", "delete".red());
35        }
36        if super::remove_file_if_exists(
37            &project_root.join("backend/src/entities/background_task.rs"),
38        )? {
39            println!(
40                "  {} backend/src/entities/background_task.rs",
41                "delete".red()
42            );
43        }
44
45        // Remove mod declarations from main.rs
46        super::remove_mod_from_main(project_root, "tasks")?;
47        super::remove_mod_from_main(project_root, "scheduler")?;
48
49        // Remove from entities/mod.rs
50        super::remove_line_from_file(
51            &project_root.join("backend/src/entities/mod.rs"),
52            "pub mod background_task;",
53        )?;
54
55        // Remove feature flag
56        super::remove_feature_flag(project_root, "background_tasks")?;
57
58        // Regenerate AI context
59        crate::ai_context::regenerate(project_root).ok();
60
61        println!();
62        println!(
63            "{}",
64            "Background tasks uninstalled successfully.".green().bold()
65        );
66
67        Ok(())
68    }
69}
70
71fn install_tasks(project_root: &Path) -> Result<()> {
72    use crate::template::TemplateEngine;
73    use crate::utils;
74    use colored::Colorize;
75    use tera::Context;
76
77    println!("{}", "Installing background tasks...".bold());
78
79    let engine = TemplateEngine::new()?;
80    let timestamp = crate::generator::migration::next_timestamp();
81
82    let mut ctx = Context::new();
83    ctx.insert("timestamp", &timestamp);
84
85    // Generate tasks module
86    let content = engine.render("addon/tasks/tasks.rs.tera", &ctx)?;
87    utils::write_file(&project_root.join("backend/src/tasks.rs"), &content)?;
88    println!("  {} backend/src/tasks.rs", "create".green());
89
90    // Generate background_task entity model
91    let content = engine.render("addon/tasks/model.rs.tera", &ctx)?;
92    utils::write_file(
93        &project_root.join("backend/src/entities/background_task.rs"),
94        &content,
95    )?;
96    println!(
97        "  {} backend/src/entities/background_task.rs",
98        "create".green()
99    );
100
101    // Generate migration
102    let content = engine.render("addon/tasks/migration.rs.tera", &ctx)?;
103    let migration_module = format!("m{}_create_background_tasks_table", timestamp);
104    utils::write_file(
105        &project_root.join(format!("backend/migration/src/{}.rs", migration_module)),
106        &content,
107    )?;
108    println!(
109        "  {} backend/migration/src/{}.rs",
110        "create".green(),
111        migration_module
112    );
113
114    // Register entity module
115    let mods_marker = "// === ROMANCE:MODS ===";
116    utils::insert_at_marker(
117        &project_root.join("backend/src/entities/mod.rs"),
118        mods_marker,
119        "pub mod background_task;",
120    )?;
121
122    // Register migration
123    let lib_path = project_root.join("backend/migration/src/lib.rs");
124    utils::insert_at_marker(
125        &lib_path,
126        "// === ROMANCE:MIGRATION_MODS ===",
127        &format!("mod {};", migration_module),
128    )?;
129    utils::insert_at_marker(
130        &lib_path,
131        "// === ROMANCE:MIGRATIONS ===",
132        &format!("            Box::new({}::Migration),", migration_module),
133    )?;
134
135    // Generate scheduler module
136    let content = engine.render("addon/tasks/scheduler.rs.tera", &ctx)?;
137    utils::write_file(&project_root.join("backend/src/scheduler.rs"), &content)?;
138    println!("  {} backend/src/scheduler.rs", "create".green());
139
140    // Add mod tasks and mod scheduler to main.rs
141    super::add_mod_to_main(project_root, "tasks")?;
142    super::add_mod_to_main(project_root, "scheduler")?;
143
144    // Update romance.toml
145    super::update_feature_flag(project_root, "background_tasks", true)?;
146
147    println!();
148    println!(
149        "{}",
150        "Background tasks installed successfully!".green().bold()
151    );
152    println!("  Run migrations: romance db migrate");
153    println!("  Enqueue tasks: TaskQueue::new(db).enqueue(\"send_email\", payload).await?");
154    println!("  Start worker: TaskQueue::new(db).start_worker(4, handler).await");
155    println!();
156    println!("  Scheduler (recurring jobs):");
157    println!("    let mut scheduler = scheduler::Scheduler::new();");
158    println!("    scheduler.add_job(\"cleanup\", Duration::from_secs(3600), || {{");
159    println!("        tokio::spawn(async {{ /* ... */ }})");
160    println!("    }});");
161    println!("    scheduler.start();");
162
163    Ok(())
164}