diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 0ae24ac..0000000 --- a/rustfmt.toml +++ /dev/null @@ -1,2 +0,0 @@ -# merge_imports = true -# space_after_colon = true diff --git a/src/banking/mod.rs b/src/banking/mod.rs index 9cf23b7..4ec7374 100644 --- a/src/banking/mod.rs +++ b/src/banking/mod.rs @@ -34,6 +34,11 @@ pub trait Banking: AsRef { sum % 10 == 0 } + /// Extract all the card numbers. + fn extract_card_numbers(&self) -> Vec { + todo!() + } + /// Get the bank name from card number. fn get_bank_name_from_card_number(&self) -> Option<&str> { let number = self.as_ref(); diff --git a/src/banking/sheba.rs b/src/banking/sheba.rs index 45129c6..b5e5e63 100644 --- a/src/banking/sheba.rs +++ b/src/banking/sheba.rs @@ -7,11 +7,15 @@ pub trait ShebaNumber: AsRef { self.iso_7064_mod_97_10().is_ok_and(|i| i == 1) } - fn sheba_code_info(&self) -> Option<&ShebaInfo> { + fn get_sheba_info(&self) -> Option { if !self.is_valid_sheba_code() { return None; } - SHEBA_CODE_TABLE.get(&&self.as_ref()[4..7]) + + let digits = self.as_ref(); + SHEBA_CODE_TABLE + .get(&&digits[4..7]) + .map(|sc| sc.process(digits)) } fn iso_7064_mod_97_10(&self) -> crate::Result { @@ -45,6 +49,10 @@ impl_trait_for_string_types!(ShebaNumber); #[cfg(test)] mod sheba_code { + use crate::banking::sheba_table::{ + process_parsian, process_pasargad, process_shahr, ShebaAccountNumber, + }; + use super::*; #[test] @@ -57,6 +65,55 @@ mod sheba_code { #[test] fn sheba_code_info() { - assert!("IR210180000000009190404878".sheba_code_info().is_some()) + assert!("IR210180000000009190404878".get_sheba_info().is_some()); + assert!("IR012345678901234567890123".get_sheba_info().is_none()); + assert!("IR012345678A01234567890123".get_sheba_info().is_none()); + + let sheba_info_pasargad = ShebaInfo { + code: "057", + nickname: "pasargad", + name: "Pasargad Bank", + persian_name: "بانک پاسارگاد", + account_number: Some(ShebaAccountNumber { + normal: "220800134473701".to_owned(), + formatted: "220-800-13447370-1".to_owned(), + }), + process: Some(process_pasargad), + }; + let sheba_info_shahr = ShebaInfo { + code: "061", + nickname: "shahr", + name: "City Bank", + persian_name: "بانک شهر", + account_number: Some(ShebaAccountNumber { + normal: "700796858044".to_owned(), + formatted: "700796858044".to_owned(), + }), + process: Some(process_shahr), + }; + let sheba_info_parsian = ShebaInfo { + code: "054", + nickname: "parsian", + name: "Parsian Bank", + persian_name: "بانک پارسیان", + account_number: Some(ShebaAccountNumber { + normal: "020817909002".to_owned(), + formatted: "002-00817909-002".to_owned(), + }), + process: Some(process_parsian), + }; + + assert_eq!( + "IR550570022080013447370101".get_sheba_info(), + Some(sheba_info_pasargad) + ); + assert_eq!( + "IR790610000000700796858044".get_sheba_info(), + Some(sheba_info_shahr) + ); + assert_eq!( + "IR820540102680020817909002".get_sheba_info(), + Some(sheba_info_parsian) + ); } } diff --git a/src/banking/sheba_table.rs b/src/banking/sheba_table.rs index ca93dea..7e1d5e6 100644 --- a/src/banking/sheba_table.rs +++ b/src/banking/sheba_table.rs @@ -1,240 +1,379 @@ use crate::utils::{create_fixed_map, FixedMap}; -#[derive(Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ShebaAccountNumber { + pub normal: String, + pub formatted: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] pub struct ShebaInfo { pub code: &'static str, pub nickname: &'static str, pub name: &'static str, pub persian_name: &'static str, + pub account_number: Option, + pub process: Option, +} + +impl ShebaInfo { + /// Process the sheba if needed and return a newly created [`ShebaInfo`]. + pub fn process(&self, sheba_code: &str) -> ShebaInfo { + let mut sheba_clone = self.clone(); + if let Some(process) = self.process { + process(&mut sheba_clone, sheba_code); + } + sheba_clone + } } pub static SHEBA_CODE_TABLE: FixedMap<&str, ShebaInfo> = create_fixed_map! { "010" => ShebaInfo{ + code: "010", nickname: "central-bank", name: "Central Bank of Iran", persian_name: "بانک مرکزی جمهوری اسلامی ایران", - code: "010", + account_number: None, + process: None, }, "011" => ShebaInfo{ + code: "011", nickname: "sanat-o-madan", name: "Sanat O Madan Bank", persian_name: "بانک صنعت و معدن", - code: "011", + account_number: None, + process: None, }, "012" => ShebaInfo{ + code: "012", nickname: "mellat", name: "Mellat Bank", persian_name: "بانک ملت", - code: "012", + account_number: None, + process: None, }, "013" => ShebaInfo{ + code: "013", nickname: "refah", name: "Refah Bank", persian_name: "بانک رفاه کارگران", - code: "013", + account_number: None, + process: None, }, "014" => ShebaInfo{ + code: "014", nickname: "maskan", name: "Maskan Bank", persian_name: "بانک مسکن", - code: "014", + account_number: None, + process: None, }, "015" => ShebaInfo{ + code: "015", nickname: "sepah", name: "Sepah Bank", persian_name: "بانک سپه", - code: "015", + account_number: None, + process: None, }, "016" => ShebaInfo{ + code: "016", nickname: "keshavarzi", name: "Keshavarzi", persian_name: "بانک کشاورزی", - code: "016", + account_number: None, + process: None, }, "017" => ShebaInfo{ + code: "017", nickname: "melli", name: "Melli", persian_name: "بانک ملی ایران", - code: "017", + account_number: None, + process: None, }, "018" => ShebaInfo{ + code: "018", nickname: "tejarat", name: "Tejarat Bank", persian_name: "بانک تجارت", - code: "018", + account_number: None, + process: None, }, "019" => ShebaInfo{ + code: "019", nickname: "saderat", name: "Saderat Bank", persian_name: "بانک صادرات ایران", - code: "019", + account_number: None, + process: None, }, "020" => ShebaInfo{ + code: "020", nickname: "tosee-saderat", name: "Tose Saderat Bank", persian_name: "بانک توسعه صادرات", - code: "020", + account_number: None, + process: None, }, "021" => ShebaInfo{ + code: "021", nickname: "post", name: "Post Bank", persian_name: "پست بانک ایران", - code: "021", + account_number: None, + process: None, }, "022" => ShebaInfo{ + code: "022", nickname: "toose-taavon", name: "Tosee Taavon Bank", persian_name: "بانک توسعه تعاون", - code: "022", + account_number: None, + process: None, }, "051" => ShebaInfo{ + code: "051", nickname: "tosee", name: "Tosee Bank", persian_name: "موسسه اعتباری توسعه", - code: "051", + account_number: None, + process: None, }, "052" => ShebaInfo{ + code: "052", nickname: "ghavamin", name: "Ghavamin Bank", persian_name: "بانک قوامین", - code: "052", + account_number: None, + process: None, }, "053" => ShebaInfo{ + code: "053", nickname: "karafarin", name: "Karafarin Bank", persian_name: "بانک کارآفرین", - code: "053", + account_number: None, + process: None, }, "054" => ShebaInfo{ + code: "054", nickname: "parsian", name: "Parsian Bank", persian_name: "بانک پارسیان", - code: "054", + account_number: None, + process: Some(process_parsian), }, "055" => ShebaInfo{ + code: "055", nickname: "eghtesad-novin", name: "Eghtesad Novin Bank", persian_name: "بانک اقتصاد نوین", - code: "055", + account_number: None, + process: None, }, "056" => ShebaInfo{ + code: "056", nickname: "saman", name: "Saman Bank", persian_name: "بانک سامان", - code: "056", + account_number: None, + process: None, }, "057" => ShebaInfo{ + code: "057", nickname: "pasargad", name: "Pasargad Bank", persian_name: "بانک پاسارگاد", - code: "057", + account_number: None, + process: Some(process_pasargad), }, "058" => ShebaInfo{ + code: "058", nickname: "sarmayeh", name: "Sarmayeh Bank", persian_name: "بانک سرمایه", - code: "058", + account_number: None, + process: None, }, "059" => ShebaInfo{ + code: "059", nickname: "sina", name: "Sina Bank", persian_name: "بانک سینا", - code: "059", + account_number: None, + process: None, }, "060" => ShebaInfo{ + code: "060", nickname: "mehr-iran", name: "Mehr Iran Bank", persian_name: "بانک مهر ایران", - code: "060", + account_number: None, + process: None, }, "061" => ShebaInfo{ + code: "061", nickname: "shahr", name: "City Bank", persian_name: "بانک شهر", - code: "061", + account_number: None, + process: Some(process_shahr), }, "062" => ShebaInfo{ + code: "062", nickname: "ayandeh", name: "Ayandeh Bank", persian_name: "بانک آینده", - code: "062", + account_number: None, + process: None, }, "063" => ShebaInfo{ + code: "063", nickname: "ansar", name: "Ansar Bank", persian_name: "بانک انصار", - code: "063", + account_number: None, + process: None, }, "064" => ShebaInfo{ + code: "064", nickname: "gardeshgari", name: "Gardeshgari Bank", persian_name: "بانک گردشگری", - code: "064", + account_number: None, + process: None, }, "065" => ShebaInfo{ + code: "065", nickname: "hekmat-iranian", name: "Hekmat Iranian Bank", persian_name: "بانک حکمت ایرانیان", - code: "065", + account_number: None, + process: None, }, "066" => ShebaInfo{ + code: "066", nickname: "dey", name: "Dey Bank", persian_name: "بانک دی", - code: "066", + account_number: None, + process: None, }, "069" => ShebaInfo{ + code: "069", nickname: "iran-zamin", name: "Iran Zamin Bank", persian_name: "بانک ایران زمین", - code: "069", + account_number: None, + process: None, }, "070" => ShebaInfo{ + code: "070", nickname: "resalat", name: "Resalat Bank", persian_name: "بانک قرض الحسنه رسالت", - code: "070", + account_number: None, + process: None, }, "073" => ShebaInfo{ + code: "073", nickname: "kosar", name: "Kosar Credit Institute", persian_name: "موسسه اعتباری کوثر", - code: "073", + account_number: None, + process: None, }, "075" => ShebaInfo{ + code: "075", nickname: "melal", name: "Melal Credit Institute", persian_name: "موسسه اعتباری ملل", - code: "075", + account_number: None, + process: None, }, "078" => ShebaInfo{ + code: "078", nickname: "middle-east-bank", name: "Middle East Bank", persian_name: "بانک خاورمیانه", - code: "078", + account_number: None, + process: None, }, "080" => ShebaInfo{ + code: "080", nickname: "noor-bank", name: "Noor Credit Institution", persian_name: "موسسه اعتباری نور", - code: "080", + account_number: None, + process: None, }, "079" => ShebaInfo{ + code: "079", nickname: "mehr-eqtesad", name: "Mehr Eqtesad Bank", persian_name: "بانک مهر اقتصاد", - code: "079", + account_number: None, + process: None, }, "090" => ShebaInfo{ + code: "090", nickname: "mehr-iran", name: "Mehr Iran Bank", persian_name: "بانک مهر ایران", - code: "090", + account_number: None, + process: None, }, "095" => ShebaInfo{ + code: "095", nickname: "iran-venezuela", name: "Iran and Venezuela Bank", persian_name: "بانک ایران و ونزوئلا", - code: "095", + account_number: None, + process: None, }, }; + +pub(super) fn process_parsian(sheba: &mut ShebaInfo, sheba_code: &str) { + let substr = &sheba_code[14..]; + sheba.account_number = Some(ShebaAccountNumber { + normal: substr.to_owned(), + formatted: format!("0{}-0{}-{}", &substr[0..2], &substr[2..9], &substr[9..12]), + }); +} + +pub(super) fn process_pasargad(sheba: &mut ShebaInfo, sheba_code: &str) { + let mut idx = 7; + for ch in sheba_code[7..].chars() { + if ch != '0' { + break; + } + idx += 1; + } + let substr = &sheba_code[idx..sheba_code.len() - 2]; + sheba.account_number = Some(ShebaAccountNumber { + normal: substr.to_owned(), + formatted: format!( + "{}-{}-{}-{}", + &substr[0..3], + &substr[3..6], + &substr[6..14], + &substr[14..15] + ), + }); +} + +pub(super) fn process_shahr(sheba: &mut ShebaInfo, sheba_code: &str) { + let mut idx = 7; + for ch in sheba_code[7..].chars() { + if ch != '0' { + break; + } + idx += 1; + } + let substr = &sheba_code[idx..]; + sheba.account_number = Some(ShebaAccountNumber { + normal: substr.to_owned(), + formatted: substr.to_owned(), + }); +} diff --git a/src/persian_content/mod.rs b/src/persian_content/mod.rs index 1e215bc..989e7df 100644 --- a/src/persian_content/mod.rs +++ b/src/persian_content/mod.rs @@ -22,18 +22,18 @@ pub trait PersianContent: AsRef { /// Calculates how much of the text is in Persian Alphabet. /// It doesn't count the numbers and other non-alphabetical chars like " « , ، fn persian_percentage(&self) -> u8 { - let (persian_chars_len, len) = self - .as_ref() - .chars() - .fold((0u32, 0u32), |(mut pc, mut len), c| { - if c.is_alphabetic() { - if HAS_PERSIAN_CHAR.contains(&c) { - pc += 1; + let (persian_chars_len, len) = + self.as_ref() + .chars() + .fold((0u32, 0u32), |(mut pc, mut len), c| { + if c.is_alphabetic() { + if HAS_PERSIAN_CHAR.contains(&c) { + pc += 1; + } + len += 1; } - len += 1; - } - (pc, len) - }); + (pc, len) + }); (persian_chars_len * 100).checked_div(len).unwrap_or(100) as u8 } diff --git a/src/phone_number/landline.rs b/src/phone_number/landline.rs index 3c9522e..8f99996 100644 --- a/src/phone_number/landline.rs +++ b/src/phone_number/landline.rs @@ -18,7 +18,8 @@ pub trait LandlineNumber: AsRef { let mut chars = text.chars().skip(skip); - chars.by_ref().take(2).all(|c| ('1'..='9').contains(&c)) && chars.all(|c| c.is_ascii_digit()) + chars.by_ref().take(2).all(|c| ('1'..='9').contains(&c)) + && chars.all(|c| c.is_ascii_digit()) } /// Get three-digit prefix of a landline number. diff --git a/src/phone_number/mobile.rs b/src/phone_number/mobile.rs index e17e777..4a9cabb 100644 --- a/src/phone_number/mobile.rs +++ b/src/phone_number/mobile.rs @@ -98,10 +98,14 @@ pub trait MobileNumber: AsRef { let number = format!("0{}", &text[skip..]); - IRAN_MOBILE_OPERATORS.iter().find_map(|(k, v)| { - v.iter().any(|x| x == &&number[..x.len()]).then(|| IranMobileOperator::from_str(k).unwrap()) - }) - .ok_or("Can't find the operator".into()) + IRAN_MOBILE_OPERATORS + .iter() + .find_map(|(k, v)| { + v.iter() + .any(|x| x == &&number[..x.len()]) + .then(|| IranMobileOperator::from_str(k).unwrap()) + }) + .ok_or("Can't find the operator".into()) } }