written_numbers/
lib.rs

1use pomsky_macro::pomsky;
2use serde::{Deserialize, Serialize};
3
4use std::collections::HashMap;
5
6use lazy_static::lazy_static;
7use regex::Regex;
8
9pub mod english;
10pub mod latin;
11pub mod util;
12
13pub type LanguageOptions = HashMap<String, String>;
14pub type ToWordsReturn = Result<String, ToWordsError>;
15pub type LanguagesMap =
16	HashMap<String, fn(&str, &LanguageOptions) -> ToWordsReturn>;
17
18lazy_static! {
19	static ref IS_NUMBER_REGEX: Regex = Regex::new(
20		pomsky! { ^ "-"? (([d]+ "."?) | ("." [d]+) | ([d]+ "." [d]+)) $ }
21	)
22	.unwrap();
23}
24
25#[derive(Serialize, Deserialize)]
26pub struct ToWordsOptions {
27	pub language: String,
28}
29
30#[derive(Serialize, Deserialize)]
31pub enum ToWordsError {
32	NotANumber,
33	UnimplementedLanguage,
34}
35
36fn add_default_languages(languages: &mut LanguagesMap) {
37	if !languages.contains_key("en") {
38		languages.insert("en".to_string(), english::to_words);
39	}
40	if !languages.contains_key("la") {
41		languages.insert("la".to_string(), latin::to_words);
42	}
43}
44
45pub fn to_words(
46	number: &str,
47	options: &ToWordsOptions,
48	language_options: &LanguageOptions,
49	languages: &mut LanguagesMap,
50) -> Result<String, ToWordsError> {
51	if !IS_NUMBER_REGEX.is_match(number) {
52		return Err(ToWordsError::NotANumber);
53	}
54
55	add_default_languages(languages);
56
57	let mut parsed_number = number
58		.trim_start_matches('-')
59		.trim_start_matches('0')
60		.to_string();
61	if parsed_number.starts_with('.') {
62		parsed_number = format!("0{}", parsed_number);
63	}
64	if parsed_number.len() == 0 {
65		parsed_number = "0".to_string();
66	}
67	if number.starts_with('-') {
68		parsed_number = format!("-{}", parsed_number);
69	}
70	if parsed_number.contains('.') {
71		parsed_number = parsed_number.trim_end_matches('0').to_string();
72		if parsed_number.ends_with('.') {
73			parsed_number.pop();
74		}
75	}
76
77	match languages.get(&options.language) {
78		Some(language) => (language)(&parsed_number, language_options),
79		None => Err(ToWordsError::UnimplementedLanguage),
80	}
81}