Skip to main content

skilllite_fs/
lib.rs

1//! SkillLite FS: 中心化文件操作
2//!
3//! 模块:
4//! - read_write: read_file, write_file, append_file, atomic_write
5//! - dir: read_dir, list_directory, file_exists, create_dir_all, copy, rename, remove_file, modified_time
6//! - grep: grep_directory
7//! - search_replace: apply_search_replace, apply_replace_fuzzy, insert_lines_at
8//! - backup: backup_file, prune_oldest_files
9//! - util: is_likely_binary, matches_glob
10
11mod backup;
12mod dir;
13mod grep;
14mod read_write;
15mod search_replace;
16mod util;
17
18// Re-export public API
19pub use backup::{backup_file, prune_oldest_files};
20pub use dir::{
21    copy, create_dir_all, file_exists, list_directory, modified_time, read_dir, remove_file,
22    rename, PathKind,
23};
24pub use grep::{grep_directory, GrepMatch, SKIP_DIRS};
25pub use read_write::{
26    append_file, atomic_write, read_bytes, read_bytes_limit, read_file,
27    search_replace as search_replace_file, write_file,
28};
29pub use search_replace::{
30    apply_replace_fuzzy, apply_replace_normalize_whitespace, apply_search_replace,
31    build_failure_hint, insert_lines_at, line_byte_offsets, safe_excerpt, FuzzyReplaceResult,
32};
33pub use util::{is_likely_binary, matches_glob};
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38    use std::io::Write;
39    use tempfile::{NamedTempFile, TempDir};
40
41    #[test]
42    fn test_file_exists() {
43        let dir = TempDir::new().unwrap();
44        let f = dir.path().join("a.txt");
45        std::fs::write(&f, "x").unwrap();
46        assert!(matches!(file_exists(&f).unwrap(), PathKind::File(1)));
47        assert!(matches!(file_exists(dir.path()).unwrap(), PathKind::Dir));
48        assert!(matches!(
49            file_exists(&dir.path().join("nonexistent")).unwrap(),
50            PathKind::NotFound
51        ));
52    }
53
54    #[test]
55    fn test_list_directory() {
56        let dir = TempDir::new().unwrap();
57        std::fs::write(dir.path().join("a.txt"), "1").unwrap();
58        std::fs::create_dir(dir.path().join("sub")).unwrap();
59        std::fs::write(dir.path().join("sub").join("b.txt"), "2").unwrap();
60        let entries = list_directory(dir.path(), false).unwrap();
61        assert!(entries.iter().any(|e| e.contains("a.txt")));
62        assert!(entries.iter().any(|e| e.contains("sub")));
63        let rec = list_directory(dir.path(), true).unwrap();
64        assert!(rec.iter().any(|e| e.contains("b.txt")));
65    }
66
67    #[test]
68    fn test_apply_search_replace_once() {
69        let content = "hello world\nhello rust";
70        let (new_content, count) = apply_search_replace(content, "hello", "hi", false).unwrap();
71        assert_eq!(new_content, "hi world\nhello rust");
72        assert_eq!(count, 1);
73    }
74
75    #[test]
76    fn test_apply_search_replace_all() {
77        let content = "hello world hello";
78        let (new_content, count) = apply_search_replace(content, "hello", "hi", true).unwrap();
79        assert_eq!(new_content, "hi world hi");
80        assert_eq!(count, 2);
81    }
82
83    #[test]
84    fn test_apply_search_replace_not_found() {
85        let content = "hello world";
86        let r = apply_search_replace(content, "xyz", "a", false);
87        assert!(r.is_err());
88    }
89
90    #[test]
91    fn test_search_replace_file() {
92        let mut f = NamedTempFile::new().unwrap();
93        write!(f, "foo bar foo").unwrap();
94        let path = f.path();
95        let count = search_replace_file(path, "foo", "baz", true).unwrap();
96        assert_eq!(count, 2);
97        let content = read_file(path).unwrap();
98        assert_eq!(content, "baz bar baz");
99    }
100}