codetether_agent/tui/utils/
workspace_entries.rs1use std::path::Path;
4
5use super::workspace_helpers::should_skip_entry;
6use super::workspace_types::{WorkspaceEntry, WorkspaceEntryKind};
7
8pub fn collect_entries(root: &Path) -> Vec<WorkspaceEntry> {
9 let Ok(rd) = std::fs::read_dir(root) else {
10 return Vec::new();
11 };
12 rd.flatten()
13 .filter_map(|entry| {
14 let name = entry
18 .file_name()
19 .into_string()
20 .unwrap_or_else(|os| os.to_string_lossy().into_owned());
21 if should_skip_entry(&name) {
22 return None;
23 }
24 let kind = match entry.file_type() {
25 Ok(ft) if ft.is_dir() => WorkspaceEntryKind::Directory,
26 _ => WorkspaceEntryKind::File,
27 };
28 Some(WorkspaceEntry { name, kind })
29 })
30 .collect()
31}
32
33pub fn sort_entries(entries: &mut [WorkspaceEntry]) {
34 fn cmp_ascii_ci(a: &str, b: &str) -> std::cmp::Ordering {
39 let ab = a.as_bytes();
40 let bb = b.as_bytes();
41 let len = ab.len().min(bb.len());
42 for i in 0..len {
43 let av = ab[i].to_ascii_lowercase();
44 let bv = bb[i].to_ascii_lowercase();
45 if av != bv {
46 return av.cmp(&bv);
47 }
48 }
49 ab.len().cmp(&bb.len())
50 }
51 entries.sort_by(|a, b| match (a.kind, b.kind) {
52 (WorkspaceEntryKind::Directory, WorkspaceEntryKind::File) => std::cmp::Ordering::Less,
53 (WorkspaceEntryKind::File, WorkspaceEntryKind::Directory) => std::cmp::Ordering::Greater,
54 _ => cmp_ascii_ci(&a.name, &b.name),
55 });
56}