flatten_rust/
exclusions.rs1use crate::config::GitignoreManager;
7use anyhow::Result;
8use std::collections::HashSet;
9use std::path::Path;
10use std::sync::Arc;
11use tokio::sync::RwLock;
12
13pub struct ExclusionManager {
18 gitignore_manager: Arc<RwLock<GitignoreManager>>,
19 custom_patterns: HashSet<String>,
20 enabled_templates: HashSet<String>,
21}
22
23impl ExclusionManager {
24 pub async fn new() -> Result<Self> {
40 let mut gitignore_manager = GitignoreManager::new()?;
41
42 gitignore_manager.update_if_needed().await?;
44
45 Ok(Self {
46 gitignore_manager: Arc::new(RwLock::new(gitignore_manager)),
47 custom_patterns: HashSet::new(),
48 enabled_templates: HashSet::new(),
49 })
50 }
51
52 pub async fn enable_templates_for_project(&mut self, project_path: &Path) -> Result<()> {
54 let manager = self.gitignore_manager.read().await;
55 let available_templates = manager.get_available_templates();
56
57 self.enabled_templates.clear();
59
60 for template_key in available_templates {
62 if self.detect_template_in_project(project_path, template_key).await? {
63 self.enabled_templates.insert(template_key.to_string());
64 }
65 }
66
67 Ok(())
68 }
69
70 async fn detect_template_in_project(&self, project_path: &Path, template_key: &str) -> Result<bool> {
72 let manager = self.gitignore_manager.read().await;
73
74 let patterns = manager.get_patterns_for_templates(&[template_key.to_string()]);
76
77 for pattern in patterns {
79 if self.pattern_exists_in_project(project_path, &pattern) {
80 return Ok(true);
81 }
82 }
83
84 Ok(false)
85 }
86
87 fn pattern_exists_in_project(&self, project_path: &Path, pattern: &str) -> bool {
89 match pattern.trim() {
91 p if p.ends_with("Cargo.toml") => project_path.join("Cargo.toml").exists(),
93 p if p.ends_with("package.json") => project_path.join("package.json").exists(),
94 p if p.ends_with("requirements.txt") => project_path.join("requirements.txt").exists(),
95 p if p.ends_with("pom.xml") => project_path.join("pom.xml").exists(),
96 p if p.ends_with("build.gradle") => project_path.join("build.gradle").exists(),
97 p if p.ends_with("*.csproj") => {
98 std::fs::read_dir(project_path)
100 .map(|entries| {
101 entries.filter_map(|e| e.ok())
102 .any(|e| e.file_name().to_string_lossy().ends_with(".csproj"))
103 })
104 .unwrap_or(false)
105 }
106 p if p.ends_with("go.mod") => project_path.join("go.mod").exists(),
107 p if p.ends_with("Gemfile") => project_path.join("Gemfile").exists(),
108 p if p.ends_with("composer.json") => project_path.join("composer.json").exists(),
109 p if p.ends_with("pubspec.yaml") => project_path.join("pubspec.yaml").exists(),
110
111 p if p.contains("node_modules") => project_path.join("node_modules").exists(),
113 p if p.contains("target") => project_path.join("target").exists(),
114 p if p.contains("__pycache__") => project_path.join("__pycache__").exists(),
115 p if p.contains(".gradle") => project_path.join(".gradle").exists(),
116 p if p.contains(".vs") => project_path.join(".vs").exists(),
117 p if p.contains(".idea") => project_path.join(".idea").exists(),
118
119 _ => false,
120 }
121 }
122
123 pub async fn get_exclusion_patterns(&self) -> Vec<String> {
125 let manager = self.gitignore_manager.read().await;
126 let mut patterns = Vec::new();
127
128 let enabled_templates: Vec<String> = self.enabled_templates.iter().cloned().collect();
130 patterns.extend(manager.get_patterns_for_templates(&enabled_templates));
131
132 patterns.extend(self.custom_patterns.iter().cloned());
134
135 patterns
136 }
137
138 pub async fn get_folder_patterns(&self) -> HashSet<String> {
140 let all_patterns = self.get_exclusion_patterns().await;
141 let mut folder_patterns = HashSet::new();
142
143 for pattern in all_patterns {
144 if let Some(folder_name) = self.extract_folder_name(&pattern) {
146 folder_patterns.insert(folder_name);
147 }
148 }
149
150 folder_patterns
151 }
152
153 pub async fn get_extension_patterns(&self) -> HashSet<String> {
155 let all_patterns = self.get_exclusion_patterns().await;
156 let mut extension_patterns = HashSet::new();
157
158 for pattern in all_patterns {
159 if let Some(extension) = self.extract_extension(&pattern) {
161 extension_patterns.insert(extension);
162 }
163 }
164
165 extension_patterns
166 }
167
168 fn extract_folder_name(&self, pattern: &str) -> Option<String> {
170 let pattern = pattern.trim();
171
172 if pattern.starts_with('#') || pattern.starts_with('!') {
174 return None;
175 }
176
177 if pattern.ends_with('/') {
179 return Some(pattern.trim_end_matches('/').to_string());
180 }
181
182 if !pattern.contains('.') && !pattern.contains('*') {
184 return Some(pattern.to_string());
185 }
186
187 if let Some(last_slash) = pattern.rfind('/') {
189 let dir_part = &pattern[..last_slash];
190 if !dir_part.contains('*') && !dir_part.contains('.') {
191 return Some(dir_part.to_string());
192 }
193 }
194
195 None
196 }
197
198 fn extract_extension(&self, pattern: &str) -> Option<String> {
200 let pattern = pattern.trim();
201
202 if pattern.starts_with('#') || pattern.starts_with('!') {
204 return None;
205 }
206
207 if pattern.starts_with("*.") && !pattern.contains('/') {
209 return Some(pattern.trim_start_matches("*.").to_string());
210 }
211
212 if let Some(dot_pos) = pattern.rfind('.') {
214 let after_dot = &pattern[dot_pos + 1..];
215 if !after_dot.contains('/') && !after_dot.contains('*') {
216 return Some(after_dot.to_string());
217 }
218 }
219
220 None
221 }
222
223
224
225 pub fn get_enabled_templates(&self) -> Vec<&str> {
227 self.enabled_templates.iter().map(|s| s.as_str()).collect()
228 }
229
230 pub fn enable_template(&mut self, template_key: String) {
232 self.enabled_templates.insert(template_key);
233 }
234
235 pub fn disable_template(&mut self, template_key: &str) -> bool {
237 self.enabled_templates.remove(template_key)
238 }
239
240 pub async fn force_update_templates(&self) -> Result<()> {
242 let manager = Arc::clone(&self.gitignore_manager);
243 let mut manager = manager.write().await;
244 manager.force_update().await
245 }
246
247 pub async fn get_available_templates(&self) -> Vec<String> {
249 let manager = self.gitignore_manager.read().await;
250 manager.get_available_templates().into_iter().map(|s| s.to_string()).collect()
251 }
252
253
254
255 pub async fn set_check_internet(&self, check: bool) -> Result<()> {
257 let manager = Arc::clone(&self.gitignore_manager);
258 let mut manager = manager.write().await;
259 manager.set_check_internet(check)
260 }
261}