264 lines
8.2 KiB
Rust
264 lines
8.2 KiB
Rust
use std::io::BufRead;
|
|
|
|
pub const DEFAULT_OUTPUT_FILE: &str = "numbers.json";
|
|
const LETTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
pub struct NumberParser {
|
|
pub filepath: String,
|
|
}
|
|
|
|
impl NumberParser {
|
|
pub async fn file_dump(
|
|
&self,
|
|
) -> Result<Vec<schedtxt_models::contact::Contact>, std::io::Error> {
|
|
println!("Dumping files");
|
|
let mut objs: Vec<schedtxt_models::contact::Contact> = Vec::new();
|
|
|
|
match self.read_lines(&self.filepath).await {
|
|
Ok(lines) => {
|
|
for line in lines {
|
|
let contains_letters = line.chars().any(|c| LETTERS.contains(c));
|
|
if contains_letters {
|
|
continue;
|
|
} else {
|
|
let my_obj = schedtxt_models::contact::Contact {
|
|
phone_number: line.clone(),
|
|
..Default::default()
|
|
};
|
|
if !my_obj.phone_number.is_empty() {
|
|
objs.push(my_obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
let updated_contacts = self.update_contacts(&objs).await;
|
|
let removed_duplicates = self.remove_dups(&updated_contacts).await;
|
|
|
|
Ok(removed_duplicates)
|
|
}
|
|
Err(err) => Err(err),
|
|
}
|
|
}
|
|
|
|
pub async fn save_file(&self, numbers: Vec<schedtxt_models::contact::Contact>, filename: &str) {
|
|
match serde_json::to_string(&numbers) {
|
|
Ok(json_string) => match std::fs::write(filename, json_string) {
|
|
Ok(_) => {
|
|
println!("Saved to: {filename:?}");
|
|
}
|
|
Err(err) => {
|
|
eprintln!("Error: {err:?}");
|
|
}
|
|
},
|
|
Err(err) => {
|
|
eprintln!("Error: {err:?}");
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn print_phone_numbers(&self, contacts: &Vec<schedtxt_models::contact::Contact>) {
|
|
println!("Printing phone numbers");
|
|
|
|
for contact in contacts {
|
|
println!("Phone Number: {:?}", contact.phone_number);
|
|
}
|
|
|
|
println!("Total phone numbers: {:?}", contacts.len());
|
|
}
|
|
|
|
async fn read_lines(&self, filename: &String) -> Result<Vec<String>, std::io::Error> {
|
|
match std::fs::File::open(filename) {
|
|
Ok(file) => {
|
|
let reader = std::io::BufReader::new(file);
|
|
reader.lines().collect()
|
|
}
|
|
Err(err) => Err(err),
|
|
}
|
|
}
|
|
|
|
async fn remove_dups(
|
|
&self,
|
|
contacts: &Vec<schedtxt_models::contact::Contact>,
|
|
) -> Vec<schedtxt_models::contact::Contact> {
|
|
let mut unique_contacts: Vec<schedtxt_models::contact::Contact> = Vec::new();
|
|
let mut unique_items = std::collections::HashSet::new();
|
|
|
|
for val in contacts {
|
|
unique_items.insert(val.phone_number.clone());
|
|
}
|
|
|
|
for unique_item in unique_items {
|
|
let contact = schedtxt_models::contact::Contact {
|
|
phone_number: unique_item,
|
|
..Default::default()
|
|
};
|
|
unique_contacts.push(contact);
|
|
}
|
|
|
|
unique_contacts
|
|
}
|
|
|
|
async fn update_contacts(
|
|
&self,
|
|
contacts: &Vec<schedtxt_models::contact::Contact>,
|
|
) -> Vec<schedtxt_models::contact::Contact> {
|
|
let mut updated: Vec<schedtxt_models::contact::Contact> = Vec::new();
|
|
|
|
for ct in contacts {
|
|
let phone_number = ct.phone_number.clone();
|
|
if phone_number.len() < 2 {
|
|
println!("Invalid number: {phone_number:?}");
|
|
continue;
|
|
}
|
|
|
|
let pars = self.remove_some_data(&phone_number).await;
|
|
|
|
match self.add_prefix(&pars) {
|
|
Ok(updated_parsed) => {
|
|
let contact = schedtxt_models::contact::Contact {
|
|
phone_number: updated_parsed,
|
|
..Default::default()
|
|
};
|
|
updated.push(contact);
|
|
}
|
|
Err(err) => {
|
|
eprintln!("Error: {err:?}");
|
|
std::process::exit(-2);
|
|
}
|
|
};
|
|
}
|
|
|
|
updated
|
|
}
|
|
|
|
fn add_prefix(&self, raw: &String) -> Result<String, std::io::Error> {
|
|
if raw.is_empty() {
|
|
Err(std::io::Error::other("Empty"))
|
|
} else {
|
|
let mut parsed: String = String::new();
|
|
let first_char = raw.chars().nth(0).unwrap();
|
|
|
|
if first_char == '1' {
|
|
parsed = format!("+{raw}");
|
|
} else if first_char != '+' {
|
|
parsed = format!("+1{raw}");
|
|
}
|
|
|
|
Ok(parsed)
|
|
}
|
|
}
|
|
|
|
async fn remove_some_data(&self, unparsed: &str) -> String {
|
|
let mut parsed: String = String::new();
|
|
|
|
for ch in unparsed.chars() {
|
|
if ch == ' ' {
|
|
continue;
|
|
} else {
|
|
if ch.is_numeric() {
|
|
parsed.push(ch);
|
|
}
|
|
}
|
|
}
|
|
|
|
parsed
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use schedtxt_models::contact::Contact;
|
|
|
|
const TEST_NUMBERS_0: &str = "tests/numbers-0.txt";
|
|
|
|
#[test]
|
|
fn test_file_dump() {
|
|
let file_exists = {
|
|
let path = std::path::Path::new(TEST_NUMBERS_0);
|
|
path.exists()
|
|
};
|
|
|
|
assert_eq!(true, file_exists, "File does not exists {TEST_NUMBERS_0:?}");
|
|
|
|
let filename = String::from(TEST_NUMBERS_0);
|
|
let prsr = super::NumberParser { filepath: filename };
|
|
let original: Vec<Contact> = vec![
|
|
Contact {
|
|
phone_number: String::from("+13055550112"),
|
|
..Default::default()
|
|
},
|
|
Contact {
|
|
phone_number: String::from("+13175550123"),
|
|
..Default::default()
|
|
},
|
|
Contact {
|
|
phone_number: String::from("+14155550135"),
|
|
..Default::default()
|
|
},
|
|
Contact {
|
|
phone_number: String::from("+16085550190"),
|
|
..Default::default()
|
|
},
|
|
Contact {
|
|
phone_number: String::from("+17135550167"),
|
|
..Default::default()
|
|
},
|
|
Contact {
|
|
phone_number: String::from("+16025550177"),
|
|
..Default::default()
|
|
},
|
|
Contact {
|
|
phone_number: String::from("+13605550158"),
|
|
..Default::default()
|
|
},
|
|
Contact {
|
|
phone_number: String::from("+12175550194"),
|
|
..Default::default()
|
|
},
|
|
];
|
|
|
|
match async_std::task::block_on(prsr.file_dump()) {
|
|
Ok(contacts) => {
|
|
assert!((contacts.len() > 0), "No contacts has been parsed");
|
|
|
|
let phone_numbers_1: std::collections::HashSet<_> =
|
|
contacts.iter().map(|p| p.phone_number.clone()).collect();
|
|
let phone_numbers_2: std::collections::HashSet<_> =
|
|
original.iter().map(|p| p.phone_number.clone()).collect();
|
|
assert_eq!(phone_numbers_1, phone_numbers_2, "No match");
|
|
}
|
|
Err(err) => {
|
|
assert!(false, "Error: {err:?}");
|
|
}
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn test_save_file() {
|
|
let prsr = super::NumberParser {
|
|
filepath: String::from(TEST_NUMBERS_0),
|
|
};
|
|
let results = async_std::task::block_on(prsr.file_dump());
|
|
assert!(results.is_ok(), "File dump not successful");
|
|
let test_output_file: &str = "test-numbers.json";
|
|
|
|
let contacts = results.unwrap();
|
|
async_std::task::block_on(prsr.save_file(contacts, test_output_file));
|
|
let file_exists = {
|
|
let path = std::path::Path::new(test_output_file);
|
|
path.exists()
|
|
};
|
|
|
|
assert!(file_exists, "Contacts were not saved");
|
|
match std::fs::remove_file(test_output_file) {
|
|
Ok(_) => {
|
|
println!("File removed");
|
|
}
|
|
Err(err) => {
|
|
assert!(false, "Error: {err:?}");
|
|
}
|
|
}
|
|
}
|
|
}
|