1use std::fs::File;
2use std::io::Read;
3use std::path::Path;
4
5pub fn read_password_list(
6 filepath: &str,
7) -> Result<Vec<String>, Box<dyn std::error::Error>> {
8 let path = Path::new(filepath);
9 let extension = path.extension().and_then(|ext| ext.to_str()).unwrap_or("");
10
11 match extension.to_lowercase().as_str() {
12 "csv" => read_csv_passwords(filepath),
13 _ => read_txt_passwords(filepath),
14 }
15}
16
17fn validate_password(password: &str) -> Option<String> {
18 let trimmed = password.trim();
19 if trimmed.is_empty() {
20 None
21 } else {
22 Some(trimmed.to_string())
23 }
24}
25
26fn read_txt_passwords(
27 filepath: &str,
28) -> Result<Vec<String>, Box<dyn std::error::Error>> {
29 let mut file = File::open(filepath)?;
30 let mut contents = String::new();
31 file.read_to_string(&mut contents)?;
32 let passwords: Vec<String> =
33 contents.lines().filter_map(validate_password).collect();
34 Ok(passwords)
35}
36
37fn read_csv_passwords(
38 filepath: &str,
39) -> Result<Vec<String>, Box<dyn std::error::Error>> {
40 let file = File::open(filepath)?;
41 let mut rdr = csv::ReaderBuilder::new()
42 .has_headers(false)
43 .from_reader(file);
44 let mut passwords = Vec::new();
45
46 for result in rdr.records() {
47 let record = result?;
48 if let Some(password) = record.get(0) {
50 if let Some(validated) = validate_password(password) {
51 passwords.push(validated);
52 }
53 }
54 }
55 Ok(passwords)
56}
57
58#[cfg(test)]
59mod test_read_password_list {
60 use super::*;
61 use std::io::Write;
62 use tempfile::NamedTempFile;
63
64 #[test]
65 fn test_read_password_list_txt() {
66 let file_contents = "password1\npassword2\npassword3";
67 let mut temp_file = NamedTempFile::new().unwrap();
68 temp_file.write_all(file_contents.as_bytes()).unwrap();
69
70 let password_list =
71 read_password_list(temp_file.path().to_str().unwrap()).unwrap();
72
73 assert_eq!(password_list, vec!["password1", "password2", "password3"]);
74 }
75
76 #[test]
77 fn test_read_password_list_csv() {
78 let file_contents = "password1\npassword2\npassword3\n";
79 let mut temp_file = NamedTempFile::with_suffix(".csv").unwrap();
80 temp_file.write_all(file_contents.as_bytes()).unwrap();
81
82 let password_list =
83 read_password_list(temp_file.path().to_str().unwrap()).unwrap();
84
85 assert_eq!(password_list, vec!["password1", "password2", "password3"]);
86 }
87
88 #[test]
89 fn test_read_password_list_csv_with_headers() {
90 let file_contents = "password,description\npassword1,common\npassword2,weak\npassword3,medium\n";
91 let mut temp_file = NamedTempFile::with_suffix(".csv").unwrap();
92 temp_file.write_all(file_contents.as_bytes()).unwrap();
93
94 let password_list =
95 read_password_list(temp_file.path().to_str().unwrap()).unwrap();
96
97 assert_eq!(
98 password_list,
99 vec!["password", "password1", "password2", "password3"]
100 );
101 }
102
103 #[test]
104 fn test_read_password_list_csv_with_quotes() {
105 let file_contents = "\"password1\",\"description1\"\n\"password,2\",\"description2\"\n\"password3\",\"description3\"\n";
106 let mut temp_file = NamedTempFile::with_suffix(".csv").unwrap();
107 temp_file.write_all(file_contents.as_bytes()).unwrap();
108
109 let password_list =
110 read_password_list(temp_file.path().to_str().unwrap()).unwrap();
111
112 assert_eq!(password_list, vec!["password1", "password,2", "password3"]);
113 }
114}