romance_core/addon/
tasks.rs1use 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 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 super::remove_mod_from_main(project_root, "tasks")?;
47 super::remove_mod_from_main(project_root, "scheduler")?;
48
49 super::remove_line_from_file(
51 &project_root.join("backend/src/entities/mod.rs"),
52 "pub mod background_task;",
53 )?;
54
55 super::remove_feature_flag(project_root, "background_tasks")?;
57
58 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", ×tamp);
84
85 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 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 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 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 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 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 super::add_mod_to_main(project_root, "tasks")?;
142 super::add_mod_to_main(project_root, "scheduler")?;
143
144 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}