Skip to main content

kbolt_core/engine/
ignore_ops.rs

1use super::{
2    collection_ignore_file_path, count_ignore_patterns, validate_ignore_pattern, Engine,
3    IgnoreListEntry,
4};
5use crate::lock::LockMode;
6use crate::Result;
7use kbolt_types::KboltError;
8
9impl Engine {
10    pub fn read_collection_ignore(
11        &self,
12        space: Option<&str>,
13        collection: &str,
14    ) -> Result<(String, Option<String>)> {
15        let _lock = self.acquire_operation_lock(LockMode::Shared)?;
16        let resolved_space = self.resolve_space_row(space, Some(collection))?;
17        self.storage.get_collection(resolved_space.id, collection)?;
18
19        let path =
20            collection_ignore_file_path(&self.config.config_dir, &resolved_space.name, collection);
21        let raw = match std::fs::read_to_string(path) {
22            Ok(raw) => raw,
23            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
24                return Ok((resolved_space.name, None))
25            }
26            Err(err) => return Err(err.into()),
27        };
28
29        let trimmed = raw.trim_end_matches('\n').to_string();
30        if trimmed.trim().is_empty() {
31            return Ok((resolved_space.name, None));
32        }
33
34        Ok((resolved_space.name, Some(trimmed)))
35    }
36
37    pub fn add_collection_ignore_pattern(
38        &self,
39        space: Option<&str>,
40        collection: &str,
41        pattern: &str,
42    ) -> Result<(String, String)> {
43        let _lock = self.acquire_operation_lock(LockMode::Exclusive)?;
44        let resolved_space = self.resolve_space_row(space, Some(collection))?;
45        self.storage.get_collection(resolved_space.id, collection)?;
46
47        let normalized_pattern = validate_ignore_pattern(pattern)?;
48        let path =
49            collection_ignore_file_path(&self.config.config_dir, &resolved_space.name, collection);
50        if let Some(parent) = path.parent() {
51            std::fs::create_dir_all(parent)?;
52        }
53
54        let mut file = std::fs::OpenOptions::new()
55            .create(true)
56            .append(true)
57            .open(&path)?;
58        use std::io::Write;
59        writeln!(file, "{normalized_pattern}")?;
60
61        Ok((resolved_space.name, normalized_pattern))
62    }
63
64    pub fn remove_collection_ignore_pattern(
65        &self,
66        space: Option<&str>,
67        collection: &str,
68        pattern: &str,
69    ) -> Result<(String, usize)> {
70        let _lock = self.acquire_operation_lock(LockMode::Exclusive)?;
71        let resolved_space = self.resolve_space_row(space, Some(collection))?;
72        self.storage.get_collection(resolved_space.id, collection)?;
73
74        let normalized_pattern = validate_ignore_pattern(pattern)?;
75        let path =
76            collection_ignore_file_path(&self.config.config_dir, &resolved_space.name, collection);
77        let raw = match std::fs::read_to_string(&path) {
78            Ok(raw) => raw,
79            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
80                return Ok((resolved_space.name, 0))
81            }
82            Err(err) => return Err(err.into()),
83        };
84
85        let mut removed_count = 0usize;
86        let mut remaining = Vec::new();
87        for line in raw.lines() {
88            if line == normalized_pattern {
89                removed_count = removed_count.saturating_add(1);
90            } else {
91                remaining.push(line.to_string());
92            }
93        }
94
95        if removed_count == 0 {
96            return Ok((resolved_space.name, 0));
97        }
98
99        if remaining.is_empty() {
100            std::fs::remove_file(path)?;
101            return Ok((resolved_space.name, removed_count));
102        }
103
104        let mut content = remaining.join("\n");
105        content.push('\n');
106        std::fs::write(path, content)?;
107        Ok((resolved_space.name, removed_count))
108    }
109
110    pub fn list_collection_ignores(&self, space: Option<&str>) -> Result<Vec<IgnoreListEntry>> {
111        let _lock = self.acquire_operation_lock(LockMode::Shared)?;
112        let (space_id_filter, spaces_by_id) = if let Some(space_name) = space {
113            let resolved = self.resolve_space_row(Some(space_name), None)?;
114            let mut map = std::collections::HashMap::new();
115            map.insert(resolved.id, resolved.name.clone());
116            (Some(resolved.id), map)
117        } else {
118            let spaces = self.storage.list_spaces()?;
119            let map = spaces
120                .into_iter()
121                .map(|space| (space.id, space.name))
122                .collect::<std::collections::HashMap<_, _>>();
123            (None, map)
124        };
125
126        let collections = self.storage.list_collections(space_id_filter)?;
127        let mut entries = Vec::new();
128        for collection in collections {
129            let space_name = spaces_by_id
130                .get(&collection.space_id)
131                .ok_or_else(|| {
132                    KboltError::Internal(format!(
133                        "missing space mapping for collection '{}'",
134                        collection.name
135                    ))
136                })?
137                .clone();
138            let path =
139                collection_ignore_file_path(&self.config.config_dir, &space_name, &collection.name);
140            if !path.is_file() {
141                continue;
142            }
143
144            let raw = std::fs::read_to_string(path)?;
145            entries.push(IgnoreListEntry {
146                space: space_name,
147                collection: collection.name,
148                pattern_count: count_ignore_patterns(&raw),
149            });
150        }
151        entries.sort_by(|left, right| {
152            left.space
153                .cmp(&right.space)
154                .then(left.collection.cmp(&right.collection))
155        });
156        Ok(entries)
157    }
158
159    pub fn prepare_collection_ignore_edit(
160        &self,
161        space: Option<&str>,
162        collection: &str,
163    ) -> Result<(String, std::path::PathBuf)> {
164        let _lock = self.acquire_operation_lock(LockMode::Exclusive)?;
165        let resolved_space = self.resolve_space_row(space, Some(collection))?;
166        self.storage.get_collection(resolved_space.id, collection)?;
167
168        let path =
169            collection_ignore_file_path(&self.config.config_dir, &resolved_space.name, collection);
170        if let Some(parent) = path.parent() {
171            std::fs::create_dir_all(parent)?;
172        }
173        let _file = std::fs::OpenOptions::new()
174            .create(true)
175            .write(true)
176            .truncate(false)
177            .open(&path)?;
178
179        Ok((resolved_space.name, path))
180    }
181}