Update 2.0

Changes to formatting system

Create a formatting struct and then pass it to the
to_text macro or the
to_text_fmt function to format the strings accordingly
Updated docs for the new version
This commit is contained in:
Uttarayan Mondal
2021-03-08 23:40:19 +05:30
parent 89e4ee1442
commit cc82c47628
34 changed files with 932 additions and 392 deletions
+62
View File
@@ -0,0 +1,62 @@
/// Set the formatting of the output
#[derive(Clone, Copy)]
pub struct Formatting<'format> {
/// capitalize the start of the word.
pub capitalize: bool,
/// Set the seperator in between digits like "threehundred,twentytwo".
pub digit_seperator: Option<&'format str>,
/// Set the seperator in between words like "three/hundred".
pub place_seperator: Option<&'format str>,
/// Set the seperator between tens place digits like "twenty-two".
pub tens_seperator: Option<&'format str>,
}
impl<'format> Formatting<'format> {
/// Get the default formatting.
pub fn default() -> Self {
Self {
capitalize: true,
digit_seperator: Some(","),
place_seperator: Some(" "),
tens_seperator: Some("-"),
}
}
/// No formatting at all
pub fn none() -> Self {
Self {
capitalize: false,
digit_seperator: None,
place_seperator: None,
tens_seperator: None,
}
}
/// With same formatting for all
pub fn with_seperator(seperator: &'format str) -> Self {
Self {
capitalize: false,
digit_seperator: Some(seperator),
place_seperator: Some(seperator),
tens_seperator: Some(seperator),
}
}
/// Capitalize the formatting
pub fn capitalize(&mut self) -> Self {
self.capitalize = true;
Self {
capitalize: self.capitalize,
digit_seperator: self.digit_seperator,
place_seperator: self.place_seperator,
tens_seperator: self.tens_seperator,
}
}
/// Lower case the formatting struct
pub fn decapitalize(&mut self) -> Self {
self.capitalize = false;
Self {
capitalize: self.capitalize,
digit_seperator: self.digit_seperator,
place_seperator: self.place_seperator,
tens_seperator: self.tens_seperator,
}
}
}
+21 -56
View File
@@ -1,74 +1,39 @@
//!```rust
//!extern crate ntext;
//!use ntext::digit_to_text;
//!fn main() {
//! println!("{}",digit_to_text(1).unwrap());
//! assert_eq!("two",digit_to_text(2).unwrap());
//! assert_ne!("five",digit_to_text(8).unwrap());
//!}
//!```
//! However giving the program a zero will return an empty string.
//!
//! Example program with default formatting
//!```rust
//!extern crate ntext;
//!use ntext::to_text;
//!fn main() {
//! println!("{}",to_text!(1312));
//! assert_eq!(to_text!(1312),"onethousandthreehundredtwelve");
//! println!("{}",to_text!(7123));
//! assert_eq!(to_text!(7123," "),"seven thousand one hundred twenty three");
//! assert_eq!(to_text!(7123),"Seven Thousand,One Hundred,Twenty-Three");
//! assert_eq!(to_text!(1000),"One Thousand");
//!}
//!```
//! Example program with custom formatting
//!
//!```rust
//!extern crate ntext;
//!use ntext::{Formatting,to_text};
//!fn main() {
//! assert_eq!(to_text!(1000, &Formatting::none().capitalize()),"OneThousand");
//! assert_eq!(to_text!(34123, &Formatting::with_seperator("#").capitalize()),"Thirty#Four#Thousand#One#Hundred#Twenty#Three");
//!}
//!```
//! This macro will also return an empty string on input zero
//! You can also create the Formatting struct manually
mod no_seperator;
mod seperator;
mod formatting;
mod numtext;
mod test;
pub use no_seperator::to_text_no_seperator;
pub use seperator::to_text_with_seperator;
/// Convert digit to words in a string.
pub fn digit_to_text(digit: u8) -> Option<String> {
match digit {
0 => Some("".to_string()),
1 => Some("one".to_string()),
2 => Some("two".to_string()),
3 => Some("three".to_string()),
4 => Some("four".to_string()),
5 => Some("five".to_string()),
6 => Some("six".to_string()),
7 => Some("seven".to_string()),
8 => Some("eight".to_string()),
9 => Some("nine".to_string()),
_ => None,
}
}
fn place_value(number: u8, place: u8, seperator: Option<&str>) -> Option<String> {
let mut buffer = digit_to_text(number).unwrap();
if let Some(sep) = seperator {
if number != 0 {
buffer.push_str(sep)
}
}
match place {
1 => (),
2 => (), //This should never happen as 2 is included in the tens_place_holders
3 => buffer.push_str("hundred"),
4 => buffer.push_str("thousand"),
5 => (), // Souldn't happen
6 => buffer.push_str("million"),
_ => (),
};
Some(buffer)
}
pub use formatting::Formatting;
pub use numtext::{digit_to_text, to_text_fmt};
/// Macro which supports both seperator and without it
#[macro_export]
macro_rules! to_text {
($number:expr) => {
ntext::to_text_no_seperator($number);
ntext::to_text_fmt($number, &ntext::Formatting::default());
};
($number:expr, $seperator:expr) => {
ntext::to_text_with_seperator($number, $seperator);
($number:expr, $formatting:expr) => {
ntext::to_text_fmt($number, $formatting);
};
}
-71
View File
@@ -1,71 +0,0 @@
use crate::{digit_to_text, place_value};
fn tens_place(tens: u8, ones: u8) -> Option<String> {
match tens {
0 => digit_to_text(ones),
1 => match ones {
0 => Some("ten".to_string()),
1 => Some("eleven".to_string()),
2 => Some("twelve".to_string()),
3 => Some("thirteen".to_string()),
4 => Some("fourteen".to_string()),
5 => Some("fifteen".to_string()),
6 => Some("sixteen".to_string()),
7 => Some("seventeen".to_string()),
8 => Some("eighteen".to_string()),
9 => Some("nineteen".to_string()),
_ => None,
},
d @ 2..=5 | d @ 8 => Some({
let mut buffer = String::new();
match d {
2 => buffer.push_str("twenty"),
3 => buffer.push_str("thirty"),
4 => buffer.push_str("forty"),
5 => buffer.push_str("fifty"),
8 => buffer.push_str("eighty"),
_ => (),
}
buffer.push_str(digit_to_text(ones).unwrap().as_str());
buffer
}),
d @ 6..=9 => Some({
let mut string = digit_to_text(d).unwrap() + "ty";
string.push_str(digit_to_text(ones).unwrap().as_str());
string
}),
_ => None,
}
}
/// Convert u32 to words in a string.
pub fn to_text_no_seperator(number: u32) -> String {
let mut numtext: String = String::new();
let mut last: Option<u8> = None;
let tens_place_holders: [u8; 2] = [2, 5];
let digits: Vec<u8> = number
.to_string()
.chars()
.map(|d| d.to_digit(10).unwrap() as u8)
.collect();
let mut place: u8 = digits.len() as u8;
for (_index, digit) in digits.iter().enumerate() {
if tens_place_holders.contains(&place) {
last = Some(*digit);
place -= 1;
continue;
}
if let Some(last_digit) = last {
numtext.push_str(tens_place(last_digit, *digit).unwrap().as_str());
if place > 2 {
numtext.push_str(place_value(0, place, None).unwrap().as_str());
}
last = None;
} else if *digit != 0 {
numtext.push_str(place_value(*digit, place, None).unwrap().as_str())
}
place -= 1;
}
numtext
}
+177
View File
@@ -0,0 +1,177 @@
use crate::Formatting;
/// Convert digit to words in a string.
pub fn digit_to_text(digit: u8, capitalize: bool) -> Option<String> {
if capitalize {
match digit {
0 => Some("".to_string()),
1 => Some("One".to_string()),
2 => Some("Two".to_string()),
3 => Some("Three".to_string()),
4 => Some("Four".to_string()),
5 => Some("Five".to_string()),
6 => Some("Six".to_string()),
7 => Some("Seven".to_string()),
8 => Some("Eight".to_string()),
9 => Some("Nine".to_string()),
_ => None,
}
} else {
match digit {
0 => Some("".to_string()),
1 => Some("one".to_string()),
2 => Some("two".to_string()),
3 => Some("three".to_string()),
4 => Some("four".to_string()),
5 => Some("five".to_string()),
6 => Some("six".to_string()),
7 => Some("seven".to_string()),
8 => Some("eight".to_string()),
9 => Some("nine".to_string()),
_ => None,
}
}
}
fn place_value(number: u8, place: u8, fmt: &Formatting) -> Option<String> {
let mut buffer = digit_to_text(number, fmt.capitalize).unwrap();
if let Some(sep) = fmt.place_seperator {
if number != 0 && place != 1 {
buffer.push_str(sep)
}
}
if fmt.capitalize {
match place {
1 => (), //Can happen but should return the same digit
2 => (), //This should never happen as 2 is included in the tens_place_holders
3 => buffer.push_str("Hundred"),
4 => buffer.push_str("Thousand"),
5 => (), // Souldn't happen
6 => buffer.push_str("Million"),
_ => (),
};
} else {
match place {
1 => (), //Can happen but should return the same digit
2 => (), //This should never happen as 2 is included in the tens_place_holders
3 => buffer.push_str("hundred"),
4 => buffer.push_str("thousand"),
5 => (), // Souldn't happen
6 => buffer.push_str("million"),
_ => (),
}
}
Some(buffer)
}
fn tens_place(tens: u8, ones: u8, fmt: &Formatting) -> Option<String> {
match tens {
0 => digit_to_text(ones, fmt.capitalize),
1 => {
if fmt.capitalize {
match ones {
0 => Some("Ten".to_string()),
1 => Some("Eleven".to_string()),
2 => Some("Twelve".to_string()),
3 => Some("Thirteen".to_string()),
4 => Some("Fourteen".to_string()),
5 => Some("Fifteen".to_string()),
6 => Some("Sixteen".to_string()),
7 => Some("Seventeen".to_string()),
8 => Some("Eighteen".to_string()),
9 => Some("Nineteen".to_string()),
_ => None,
}
} else {
match ones {
0 => Some("ten".to_string()),
1 => Some("eleven".to_string()),
2 => Some("twelve".to_string()),
3 => Some("thirteen".to_string()),
4 => Some("fourteen".to_string()),
5 => Some("fifteen".to_string()),
6 => Some("sixteen".to_string()),
7 => Some("seventeen".to_string()),
8 => Some("eighteen".to_string()),
9 => Some("nineteen".to_string()),
_ => None,
}
}
}
d @ 2..=5 | d @ 8 => Some({
let mut buffer = String::new();
if fmt.capitalize {
match d {
2 => buffer.push_str("Twenty"),
3 => buffer.push_str("Thirty"),
4 => buffer.push_str("Forty"),
5 => buffer.push_str("Fifty"),
8 => buffer.push_str("Eighty"),
_ => (),
}
} else {
match d {
2 => buffer.push_str("twenty"),
3 => buffer.push_str("thirty"),
4 => buffer.push_str("forty"),
5 => buffer.push_str("fifty"),
8 => buffer.push_str("eighty"),
_ => (),
}
}
if let Some(sep) = fmt.tens_seperator {
buffer.push_str(sep);
}
buffer.push_str(digit_to_text(ones, fmt.capitalize).unwrap().as_str());
buffer
}),
d @ 6..=9 => Some({
let mut buffer = digit_to_text(d, fmt.capitalize).unwrap() + "ty";
if let Some(sep) = fmt.tens_seperator {
buffer.push_str(sep);
}
buffer.push_str(digit_to_text(ones, fmt.capitalize).unwrap().as_str());
buffer
}),
_ => None,
}
}
/// Convert usize to words in a string seperated by a seperator.
pub fn to_text_fmt(number: usize, fmt: &Formatting) -> String {
let mut numtext: String = String::new();
let mut last: Option<u8> = None;
let tens_place_holders: [u8; 2] = [2, 5];
let digits: Vec<u8> = number
.to_string()
.chars()
.map(|d| d.to_digit(10).unwrap() as u8)
.collect();
let mut place: u8 = digits.len() as u8;
for (_index, digit) in digits.iter().enumerate() {
if tens_place_holders.contains(&place) {
last = Some(*digit);
place -= 1;
continue;
}
if *digit != 0 && place != digits.len() as u8 && numtext != "" {
if let Some(sep) = fmt.digit_seperator {
numtext.push_str(sep);
}
}
if let Some(last_digit) = last {
numtext.push_str(tens_place(last_digit, *digit, fmt).unwrap().as_str());
if place > 2 {
if let Some(sep) = fmt.digit_seperator {
numtext.push_str(sep);
}
numtext.push_str(place_value(0, place, fmt).unwrap().as_str());
}
last = None;
} else if *digit != 0 {
numtext.push_str(place_value(*digit, place, fmt).unwrap().as_str());
}
place -= 1;
}
numtext
}
-86
View File
@@ -1,86 +0,0 @@
use crate::{digit_to_text, place_value};
fn tens_place_seperator(tens: u8, ones: u8, seperator: &str) -> Option<String> {
match tens {
0 => digit_to_text(ones),
1 => match ones {
0 => Some("ten".to_string()),
1 => Some("eleven".to_string()),
2 => Some("twelve".to_string()),
3 => Some("thirteen".to_string()),
4 => Some("fourteen".to_string()),
5 => Some("fifteen".to_string()),
6 => Some("sixteen".to_string()),
7 => Some("seventeen".to_string()),
8 => Some("eighteen".to_string()),
9 => Some("nineteen".to_string()),
_ => None,
},
d @ 2..=5 | d @ 8 => Some({
let mut buffer = String::new();
match d {
2 => buffer.push_str("twenty"),
3 => buffer.push_str("thirty"),
4 => buffer.push_str("forty"),
5 => buffer.push_str("fifty"),
8 => buffer.push_str("eighty"),
_ => (),
}
buffer.push_str(seperator);
buffer.push_str(digit_to_text(ones).unwrap().as_str());
buffer
}),
d @ 6..=9 => Some({
let mut buffer = digit_to_text(d).unwrap() + "ty";
buffer.push_str(seperator);
buffer.push_str(digit_to_text(ones).unwrap().as_str());
buffer
}),
_ => None,
}
}
/// Convert u32 to words in a string seperated by a seperator.
pub fn to_text_with_seperator(number: u32, seperator: &str) -> String {
let mut numtext: String = String::new();
let mut last: Option<u8> = None;
let tens_place_holders: [u8; 2] = [2, 5];
let digits: Vec<u8> = number
.to_string()
.chars()
.map(|d| d.to_digit(10).unwrap() as u8)
.collect();
let mut place: u8 = digits.len() as u8;
for (_index, digit) in digits.iter().enumerate() {
if tens_place_holders.contains(&place) {
last = Some(*digit);
place -= 1;
continue;
}
if *digit != 0 && place != digits.len() as u8 && numtext != "" {
numtext.push_str(seperator);
}
if let Some(last_digit) = last {
numtext.push_str(
tens_place_seperator(last_digit, *digit, seperator)
.unwrap()
.as_str(),
);
if place > 2 {
numtext.push_str(seperator);
numtext.push_str(place_value(0, place, Some(seperator)).unwrap().as_str());
}
last = None;
} else if *digit != 0 {
numtext.push_str(
place_value(*digit, place, Some(seperator))
.unwrap()
.as_str(),
);
}
place -= 1;
}
numtext
}
+16 -24
View File
@@ -3,34 +3,26 @@ mod tests {
#[test]
fn digits() {
use crate::digit_to_text;
assert_eq!(digit_to_text(9).unwrap(), "nine");
assert_eq!(digit_to_text(3).unwrap(), "three");
assert_eq!(digit_to_text(7).unwrap(), "seven");
assert_eq!(digit_to_text(5).unwrap(), "five");
assert_eq!(digit_to_text(9, false).unwrap(), "nine");
assert_eq!(digit_to_text(3, false).unwrap(), "three");
assert_eq!(digit_to_text(7, false).unwrap(), "seven");
assert_eq!(digit_to_text(5, false).unwrap(), "five");
assert_eq!(digit_to_text(5, true).unwrap(), "Five");
}
#[test]
fn numbers() {
use crate::to_text_no_seperator as to_text;
assert_eq!(to_text(1), "one");
assert_eq!(to_text(10), "ten");
assert_eq!(to_text(100), "onehundred");
assert_eq!(to_text(1000), "onethousand");
assert_eq!(to_text(12345), "twelvethousandthreehundredfortyfive");
assert_eq!(to_text(81123), "eightyonethousandonehundredtwentythree");
assert_eq!(to_text(12), "twelve");
}
#[test]
fn numbers_seperator() {
use crate::to_text_with_seperator as to_text;
assert_eq!(to_text(103, "/"), "one/hundred/three");
assert_eq!(to_text(1000, "/"), "one/thousand");
use crate::to_text_fmt as to_text;
use crate::Formatting;
let fmt = &Formatting::none();
assert_eq!(to_text(1, fmt), "one");
assert_eq!(to_text(10, fmt), "ten");
assert_eq!(to_text(100, fmt), "onehundred");
assert_eq!(to_text(1000, fmt), "onethousand");
assert_eq!(to_text(12345, fmt), "twelvethousandthreehundredfortyfive");
assert_eq!(
to_text(12345, "/"),
"twelve/thousand/three/hundred/forty/five"
);
assert_eq!(
to_text(651243, "/"),
"six/million/fifty/one/thousand/two/hundred/forty/three"
to_text(81123, fmt),
"eightyonethousandonehundredtwentythree"
);
assert_eq!(to_text(12, fmt), "twelve");
}
}