1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use pomsky_macro::pomsky;
use serde::{Deserialize, Serialize};

use std::collections::HashMap;

use lazy_static::lazy_static;
use regex::Regex;

pub mod english;
pub mod latin;
pub mod util;

pub type LanguageOptions = HashMap<String, String>;
pub type ToWordsReturn = Result<String, ToWordsError>;
pub type LanguagesMap =
	HashMap<String, fn(&str, &LanguageOptions) -> ToWordsReturn>;

lazy_static! {
	static ref IS_NUMBER_REGEX: Regex = Regex::new(
		pomsky! { ^ "-"? (([d]+ "."?) | ("." [d]+) | ([d]+ "." [d]+)) $ }
	)
	.unwrap();
}

#[derive(Serialize, Deserialize)]
pub struct ToWordsOptions {
	pub language: String,
}

#[derive(Serialize, Deserialize)]
pub enum ToWordsError {
	NotANumber,
	UnimplementedLanguage,
}

fn add_default_languages(languages: &mut LanguagesMap) {
	if !languages.contains_key("en") {
		languages.insert("en".to_string(), english::to_words);
	}
	if !languages.contains_key("la") {
		languages.insert("la".to_string(), latin::to_words);
	}
}

pub fn to_words(
	number: &str,
	options: &ToWordsOptions,
	language_options: &LanguageOptions,
	languages: &mut LanguagesMap,
) -> Result<String, ToWordsError> {
	if !IS_NUMBER_REGEX.is_match(number) {
		return Err(ToWordsError::NotANumber);
	}

	add_default_languages(languages);

	let mut parsed_number = number
		.trim_start_matches('-')
		.trim_start_matches('0')
		.to_string();
	if parsed_number.starts_with('.') {
		parsed_number = format!("0{}", parsed_number);
	}
	if parsed_number.len() == 0 {
		parsed_number = "0".to_string();
	}
	if number.starts_with('-') {
		parsed_number = format!("-{}", parsed_number);
	}
	if parsed_number.contains('.') {
		parsed_number = parsed_number.trim_end_matches('0').to_string();
		if parsed_number.ends_with('.') {
			parsed_number.pop();
		}
	}

	match languages.get(&options.language) {
		Some(language) => (language)(&parsed_number, language_options),
		None => Err(ToWordsError::UnimplementedLanguage),
	}
}