-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsplunk_file_locator.rs
More file actions
129 lines (118 loc) · 3.61 KB
/
Copy pathsplunk_file_locator.rs
File metadata and controls
129 lines (118 loc) · 3.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! File searching utilities for Splunk directories.
//!
//! This module provides recursive file search capabilities for locating files
//! within Splunk directory structures, with control over search depth.
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
/// Recursively search for files matching a specific filename.
///
/// Traverses the directory tree starting from `root` and collects all files
/// whose filename exactly matches `target_name`. Search depth is limited to
/// `max_depth` levels (where root = depth 0).
///
/// # Search behavior
///
/// - Does not follow symlinks
/// - Skips directories that cannot be read
/// - Returns empty vector if root doesn't exist or isn't a directory
/// - Returns `NotFound` error if root exists but no matches found
///
/// # Arguments
///
/// * `root` - The directory to start searching from
/// * `target_name` - The filename to search for (full match required)
/// * `max_depth` - Maximum directory depth to search (root = 0)
///
/// # Returns
///
/// `io::Result<Vec<PathBuf>>` - Vector of matching file paths, or error.
///
/// # Examples
///
/// ```rust,no_run
/// use splunklib_rust::splunk_file_locator;
///
/// match splunk_file_locator::find_files("/opt/splunk", "inputs.conf", 6) {
/// Ok(files) => println!("Found {} config files", files.len()),
/// Err(e) => eprintln!("Search failed: {}", e),
/// }
/// ```
pub fn find_files<P, S>(root: P, target_name: S, max_depth: usize) -> io::Result<Vec<PathBuf>>
where
P: AsRef<Path>,
S: AsRef<OsStr>,
{
let root = root.as_ref();
// If root doesn't exist or isn't a directory, mimic C++ early-return with empty hits.
if !root.try_exists()? {
return Ok(Vec::new());
}
if !fs::metadata(root)?.is_dir() {
return Ok(Vec::new());
}
let needle = target_name.as_ref();
let mut hits = Vec::new();
// walkdir counts the root as depth=1; C++ counts depth=0 at root.
// So we add 1 to the requested depth.
let walker = WalkDir::new(root)
.follow_links(false)
.max_depth(max_depth.saturating_add(1));
for entry in walker.into_iter().filter_map(Result::ok) {
let ft = entry.file_type();
// Do not follow or consider symlinks (parity with C++ is_regular_file).
if ft.is_symlink() {
continue;
}
if !ft.is_file() {
continue;
}
if entry.file_name() == needle {
hits.push(entry.into_path());
}
}
if hits.is_empty() {
Err(io::Error::new(
io::ErrorKind::NotFound,
format!(
"No files found matching {} in {}",
needle.to_string_lossy(),
root.display()
),
))
} else {
Ok(hits)
}
}
/// Convenience function with default search depth of 6.
///
/// This is a wrapper around [`find_files`] that uses the default maximum depth
/// of 6 directory levels, which is the conventional depth used in Splunk
/// installations.
///
/// # Arguments
///
/// * `root` - The directory to start searching from
/// * `target_name` - The filename to search for
///
/// # Returns
///
/// `io::Result<Vec<PathBuf>>` - Vector of matching file paths, or error.
///
/// # Examples
///
/// ```rust,no_run
/// use splunklib_rust::splunk_file_locator;
///
/// let files = splunk_file_locator::find_files_default("/opt/splunk", "server.conf")
/// .expect("Search failed");
/// ```
pub fn find_files_default<P, S>(root: P, target_name: S) -> io::Result<Vec<PathBuf>>
where
P: AsRef<Path>,
S: AsRef<OsStr>,
{
find_files(root, target_name, 6)
}