Skip to main content

romance_core/addon/
search.rs

1use crate::addon::Addon;
2use anyhow::Result;
3use std::path::Path;
4
5pub struct SearchAddon;
6
7impl Addon for SearchAddon {
8    fn name(&self) -> &str {
9        "search"
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/search.rs").exists()
18    }
19
20    fn install(&self, project_root: &Path) -> Result<()> {
21        install_search(project_root)
22    }
23
24    fn uninstall(&self, project_root: &Path) -> Result<()> {
25        use colored::Colorize;
26
27        println!("{}", "Uninstalling full-text search...".bold());
28
29        // Delete files
30        if super::remove_file_if_exists(&project_root.join("backend/src/search.rs"))? {
31            println!("  {} backend/src/search.rs", "delete".red());
32        }
33        if super::remove_file_if_exists(&project_root.join("backend/src/handlers/search.rs"))? {
34            println!("  {} backend/src/handlers/search.rs", "delete".red());
35        }
36        if super::remove_file_if_exists(
37            &project_root.join("frontend/src/components/SearchBar.tsx"),
38        )? {
39            println!(
40                "  {} frontend/src/components/SearchBar.tsx",
41                "delete".red()
42            );
43        }
44
45        // Remove mod declaration from main.rs
46        super::remove_mod_from_main(project_root, "search")?;
47
48        // Remove from handlers/mod.rs
49        super::remove_line_from_file(
50            &project_root.join("backend/src/handlers/mod.rs"),
51            "pub mod search;",
52        )?;
53
54        // Remove feature flag
55        super::remove_feature_flag(project_root, "search")?;
56
57        // Regenerate AI context
58        crate::ai_context::regenerate(project_root).ok();
59
60        println!();
61        println!(
62            "{}",
63            "Full-text search uninstalled successfully.".green().bold()
64        );
65
66        Ok(())
67    }
68}
69
70fn install_search(project_root: &Path) -> Result<()> {
71    use crate::template::TemplateEngine;
72    use crate::utils;
73    use colored::Colorize;
74    use tera::Context;
75
76    println!("{}", "Installing full-text search...".bold());
77
78    let engine = TemplateEngine::new()?;
79    let ctx = Context::new();
80
81    // Generate search module
82    let content = engine.render("addon/search/search.rs.tera", &ctx)?;
83    utils::write_file(&project_root.join("backend/src/search.rs"), &content)?;
84    println!("  {} backend/src/search.rs", "create".green());
85
86    // Generate search handler template
87    let content = engine.render("addon/search/search_handler.rs.tera", &ctx)?;
88    utils::write_file(
89        &project_root.join("backend/src/handlers/search.rs"),
90        &content,
91    )?;
92    println!("  {} backend/src/handlers/search.rs", "create".green());
93
94    // Generate frontend search component
95    let content = engine.render("addon/search/SearchBar.tsx.tera", &ctx)?;
96    utils::write_file(
97        &project_root.join("frontend/src/components/SearchBar.tsx"),
98        &content,
99    )?;
100    println!(
101        "  {} frontend/src/components/SearchBar.tsx",
102        "create".green()
103    );
104
105    // Add mod search to main.rs
106    super::add_mod_to_main(project_root, "search")?;
107
108    // Register search handler
109    let mods_marker = "// === ROMANCE:MODS ===";
110    utils::insert_at_marker(
111        &project_root.join("backend/src/handlers/mod.rs"),
112        mods_marker,
113        "pub mod search;",
114    )?;
115
116    // Update romance.toml
117    super::update_feature_flag(project_root, "search", true)?;
118
119    println!();
120    println!(
121        "{}",
122        "Full-text search installed successfully!".green().bold()
123    );
124    println!("  Use [searchable] annotation on fields: title:string[searchable]");
125    println!("  Search endpoint: GET /api/{{entities}}/search?q=term");
126
127    Ok(())
128}