kbolt_core/engine/
ignore_ops.rs1use 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}