veridian_kernel/pkg/ports/
collection.rs1#[cfg(feature = "alloc")]
9use alloc::{collections::BTreeMap, string::String, vec::Vec};
10
11#[cfg(feature = "alloc")]
12use crate::error::KernelError;
13
14#[cfg(feature = "alloc")]
16pub const STANDARD_CATEGORIES: &[&str] = &[
17 "core", "devel", "libs", "net", "security", "utils", ];
24
25#[cfg(feature = "alloc")]
31pub struct PortCollection {
32 categories: BTreeMap<String, Vec<String>>,
34 last_sync: u64,
36}
37
38#[cfg(feature = "alloc")]
39impl PortCollection {
40 pub fn new() -> Self {
43 let mut categories = BTreeMap::new();
44 for &cat in STANDARD_CATEGORIES {
45 categories.insert(String::from(cat), Vec::new());
46 }
47 Self {
48 categories,
49 last_sync: 0,
50 }
51 }
52
53 pub fn add_port(&mut self, category: &str, port_name: &str) {
56 let list = self
57 .categories
58 .entry(String::from(category))
59 .or_insert_with(Vec::new);
60
61 if !list.iter().any(|n| n == port_name) {
63 list.push(String::from(port_name));
64 }
65 }
66
67 pub fn remove_port(&mut self, category: &str, port_name: &str) -> bool {
70 if let Some(list) = self.categories.get_mut(category) {
71 if let Some(pos) = list.iter().position(|n| n == port_name) {
72 list.remove(pos);
73 return true;
74 }
75 }
76 false
77 }
78
79 pub fn list_categories(&self) -> Vec<&str> {
81 self.categories.keys().map(|s| s.as_str()).collect()
82 }
83
84 pub fn list_ports_in_category(&self, category: &str) -> Option<&[String]> {
86 self.categories.get(category).map(|v| v.as_slice())
87 }
88
89 pub fn search_ports(&self, query: &str) -> Vec<(String, String)> {
92 let query_lower = query.to_lowercase();
93 let mut results = Vec::new();
94
95 for (category, ports) in &self.categories {
96 for port_name in ports {
97 if port_name.to_lowercase().contains(&query_lower) {
98 results.push((category.clone(), port_name.clone()));
99 }
100 }
101 }
102
103 results
104 }
105
106 pub fn total_ports(&self) -> usize {
108 self.categories.values().map(|v| v.len()).sum()
109 }
110
111 pub fn category_count(&self) -> usize {
113 self.categories.len()
114 }
115
116 pub fn sync_collection(&mut self) -> Result<usize, KernelError> {
125 crate::println!("[PORTS] Syncing port collection from /usr/ports/ ...");
126
127 let vfs_count = self.scan_ports_directory();
129
130 if vfs_count > 0 {
131 self.last_sync = crate::arch::timer::get_timestamp_secs();
133
134 crate::println!(
135 "[PORTS] Sync complete (VFS): {} ports in {} categories",
136 self.total_ports(),
137 self.category_count()
138 );
139
140 return Ok(vfs_count);
141 }
142
143 crate::println!("[PORTS] No VFS ports found, loading demo ports");
146
147 let demo_ports: &[(&str, &str)] = &[
148 ("core", "coreutils"),
149 ("core", "bash"),
150 ("core", "grep"),
151 ("core", "sed"),
152 ("devel", "gcc"),
153 ("devel", "make"),
154 ("devel", "cmake"),
155 ("devel", "git"),
156 ("libs", "openssl"),
157 ("libs", "zlib"),
158 ("libs", "libpng"),
159 ("net", "curl"),
160 ("net", "wget"),
161 ("net", "openssh"),
162 ("security", "gnupg"),
163 ("security", "nmap"),
164 ("utils", "vim"),
165 ("utils", "tmux"),
166 ("utils", "htop"),
167 ];
168
169 let mut count = 0;
170 for &(category, name) in demo_ports {
171 self.add_port(category, name);
172 count += 1;
173 }
174
175 self.last_sync = crate::arch::timer::get_timestamp_secs();
176
177 crate::println!(
178 "[PORTS] Sync complete (demo): {} ports in {} categories",
179 self.total_ports(),
180 self.category_count()
181 );
182
183 Ok(count)
184 }
185
186 #[cfg_attr(not(target_arch = "x86_64"), allow(unused_variables))]
193 fn scan_ports_directory(&mut self) -> usize {
194 let vfs_lock = match crate::fs::try_get_vfs() {
195 Some(lock) => lock,
196 None => {
197 crate::println!("[PORTS] VFS not available, skipping directory scan");
198 return 0;
199 }
200 };
201
202 let vfs = vfs_lock.read();
203 let ports_root = match vfs.resolve_path("/usr/ports") {
204 Ok(node) => node,
205 Err(_) => {
206 crate::println!("[PORTS] /usr/ports not found in VFS");
207 return 0;
208 }
209 };
210
211 let category_entries = match ports_root.readdir() {
213 Ok(entries) => entries,
214 Err(_) => {
215 crate::println!("[PORTS] Cannot list /usr/ports directory");
216 return 0;
217 }
218 };
219
220 let mut total = 0;
221
222 for cat_entry in &category_entries {
223 if cat_entry.node_type != crate::fs::NodeType::Directory {
225 continue;
226 }
227
228 let category = &cat_entry.name;
229 let cat_path = alloc::format!("/usr/ports/{}", category);
230
231 let cat_node = match vfs.resolve_path(&cat_path) {
233 Ok(n) => n,
234 Err(_) => continue,
235 };
236
237 let port_entries = match cat_node.readdir() {
238 Ok(entries) => entries,
239 Err(_) => continue,
240 };
241
242 for port_entry in &port_entries {
243 if port_entry.node_type != crate::fs::NodeType::Directory {
244 continue;
245 }
246
247 self.add_port(category, &port_entry.name);
248 total += 1;
249 }
250 }
251
252 if total > 0 {
253 crate::println!(
254 "[PORTS] VFS scan discovered {} ports under /usr/ports/",
255 total
256 );
257 }
258
259 total
260 }
261
262 pub fn last_sync_time(&self) -> u64 {
264 self.last_sync
265 }
266
267 pub fn is_synced(&self) -> bool {
269 self.last_sync > 0 || self.total_ports() > 0
270 }
271}
272
273#[cfg(feature = "alloc")]
274impl Default for PortCollection {
275 fn default() -> Self {
276 Self::new()
277 }
278}