Skip to content

Commit 0337e78

Browse files
committed
Add file verification
Input paths are now parsed and all files are validated before the program begins. Also listing files are now supported.
1 parent ee7f44e commit 0337e78

File tree

3 files changed

+143
-1
lines changed

3 files changed

+143
-1
lines changed

src/util/arguments.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::chunker::chunker::ChunkerType;
22
use clap::{Parser, Subcommand, ValueEnum};
3-
use std::{path::PathBuf, usize};
3+
use std::{fs::read_to_string, path::PathBuf, usize};
44

55
#[derive(Parser)]
66
#[command(
@@ -141,6 +141,15 @@ impl TraceArgs {
141141
if self.chunkerTypes.is_empty() {
142142
self.chunkerTypes.push(ChunkerType::CDC8K);
143143
}
144+
if self.fileIsListing {
145+
self.fileNames = crate::util::fileIO::parseFileListings(
146+
self.fileNames.clone(),
147+
self.followSymlinks,
148+
)?;
149+
} else {
150+
self.fileNames =
151+
crate::util::fileIO::parseFiles(self.fileNames.clone(), self.followSymlinks)?;
152+
}
144153
self.jobs
145154
.get_or_insert(std::thread::available_parallelism().unwrap().get());
146155
if let Some(ref file) = self.progressFile {

src/util/fileIO.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use std::{fs::read_to_string, path::PathBuf};
2+
3+
pub fn parseFiles(paths: Vec<PathBuf>, followSymlinks: bool) -> Result<Vec<PathBuf>, String> {
4+
let mut files: Vec<PathBuf> = Vec::new();
5+
6+
for path in paths {
7+
let pathStr = path.to_string_lossy();
8+
9+
let exists = path.try_exists().map_err(|e| {
10+
format!(
11+
"Failed to determine if the path '{}' exists: {}",
12+
pathStr, e
13+
)
14+
})?;
15+
16+
if !exists {
17+
return Err(format!("The specified file '{}' does not exist", pathStr));
18+
} else if path.is_file() {
19+
files.push(path);
20+
} else if path.is_dir() {
21+
let entries = path.read_dir().map_err(|e| {
22+
format!("Failed to read contents of directory '{}': {}", pathStr, e)
23+
})?;
24+
let entries = entries.map(|p| p.unwrap().path()).collect();
25+
files.append(&mut parseFiles(entries, followSymlinks)?);
26+
} else if path.is_symlink() && followSymlinks {
27+
let target = path
28+
.read_link()
29+
.map_err(|e| format!("Failed to read symlink '{}': {}", pathStr, e))?;
30+
files.push(target);
31+
} else {
32+
panic!();
33+
}
34+
}
35+
36+
Ok(files)
37+
}
38+
39+
pub fn parseFileListings(
40+
listings: Vec<PathBuf>,
41+
followSymlinks: bool,
42+
) -> Result<Vec<PathBuf>, String> {
43+
let mut paths: Vec<PathBuf> = Vec::new();
44+
45+
for listing in listings {
46+
for line in read_to_string(listing.clone())
47+
.map_err(|e| {
48+
format!(
49+
"Failed to read entry from listing '{:?}': {}",
50+
listing.to_str(),
51+
e
52+
)
53+
})?
54+
.lines()
55+
{
56+
let mut path = PathBuf::new();
57+
path.push(line);
58+
59+
let pathStr = path.to_string_lossy();
60+
let exists = path.try_exists().map_err(|e| {
61+
format!(
62+
"Failed to determine if the path '{}' exists: {}",
63+
pathStr, e
64+
)
65+
})?;
66+
67+
if !exists {
68+
return Err(format!("The specified file '{}' does not exist", pathStr));
69+
} else {
70+
paths.push(path);
71+
}
72+
}
73+
}
74+
75+
parseFiles(paths, followSymlinks)
76+
}
77+
78+
#[cfg(test)]
79+
mod test {
80+
use std::os::unix::fs::symlink;
81+
82+
use super::*;
83+
use tempfile::{tempdir, tempdir_in, NamedTempFile};
84+
85+
#[test]
86+
fn testParseFiles() {
87+
let dir = tempdir().expect("Failed to create temp dir");
88+
let file = NamedTempFile::new_in(&dir).expect("Failed to create temp file");
89+
let filePath = file.path().to_path_buf();
90+
let subDir = tempdir_in(&dir).unwrap();
91+
let nestedFile = NamedTempFile::new_in(&subDir).unwrap();
92+
let nestedFilePath = nestedFile.path().to_path_buf();
93+
let subDirPath = subDir.path().to_path_buf();
94+
95+
let symlinkPath = dir.path().join("symlinkToFile");
96+
symlink(&filePath, &symlinkPath).expect("Failed to create symlink");
97+
98+
let inputPaths = vec![filePath.clone(), subDirPath.clone(), symlinkPath.clone()];
99+
let result = parseFiles(inputPaths.clone(), true).unwrap();
100+
101+
assert!(result.contains(&filePath));
102+
assert!(result.contains(&nestedFilePath));
103+
assert!(result.contains(&filePath));
104+
assert_eq!(result.len(), 3);
105+
106+
drop((file, nestedFile));
107+
}
108+
109+
#[test]
110+
fn test_parse_file_listings() {
111+
use std::fs::File;
112+
use std::io::Write;
113+
use tempfile::{tempdir, NamedTempFile};
114+
115+
let dir = tempdir().expect("Failed to create temp dir");
116+
let file1 = NamedTempFile::new_in(&dir).expect("Failed to create temp file");
117+
let file2 = NamedTempFile::new_in(&dir).expect("Failed to create temp file");
118+
let file1Path = file1.path().to_path_buf();
119+
let file2Path = file2.path().to_path_buf();
120+
121+
let listingPath = dir.path().join("listing.txt");
122+
let mut listingFile = File::create(&listingPath).expect("Failed to create listing file");
123+
writeln!(listingFile, "{}", file1Path.to_string_lossy()).unwrap();
124+
writeln!(listingFile, "{}", file2Path.to_string_lossy()).unwrap();
125+
126+
let result = parseFileListings(vec![listingPath.clone()], false).unwrap();
127+
128+
assert_eq!(result.len(), 2);
129+
assert!(result.contains(&file1Path));
130+
assert!(result.contains(&file2Path));
131+
}
132+
}

src/util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod arguments;
2+
pub mod fileIO;

0 commit comments

Comments
 (0)