Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
/// Largest integer a JavaScript number holds exactly; beyond it the three
/// languages disagree about what number was passed.
const MAX_MAGNITUDE: i64 = 9007199254740991;
const ONES: [&str; 20] = [
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen",
"seventeen", "eighteen", "nineteen",
];
const TENS: [&str; 10] = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];
/// Short scale, as used in the UK since 1974: a billion is 10^9.
const SCALES: [&str; 6] = ["", "thousand", "million", "billion", "trillion", "quadrillion"];
fn below_hundred(n: i64) -> String {
if n < 20 {
return ONES[n as usize].to_string();
}
let unit = n % 10;
if unit == 0 {
TENS[(n / 10) as usize].to_string()
} else {
format!("{}-{}", TENS[(n / 10) as usize], ONES[unit as usize])
}
}
/// One group of three digits, 1 to 999.
fn group(n: i64) -> String {
let hundreds = n / 100;
let rest = n % 100;
if hundreds == 0 {
return below_hundred(rest);
}
let head = format!("{} hundred", ONES[hundreds as usize]);
if rest == 0 {
head
} else {
format!("{} and {}", head, below_hundred(rest))
}
}
/// Spell out a whole number in British English words, cheque style:
/// 1234 is "one thousand two hundred and thirty-four".
///
/// # Panics
/// Panics if the magnitude is above 9007199254740991.
pub fn integer_to_words(value: i64) -> String {
if value > MAX_MAGNITUDE || value < -MAX_MAGNITUDE {
panic!(
"{} is out of range: integerToWords handles magnitudes up to {}",
value, MAX_MAGNITUDE
);
}
if value == 0 {
return "zero".to_string();
}
// Groups of three, least significant first.
let mut groups: Vec<i64> = Vec::new();
let mut remaining = value.abs();
while remaining > 0 {
groups.push(remaining % 1000);
remaining /= 1000;
}
let mut words: Vec<String> = Vec::new();
for i in (0..groups.len()).rev() {
let g = groups[i];
if g == 0 {
continue;
}
let mut text = group(g);
if i > 0 {
text = format!("{} {}", text, SCALES[i]);
} else if groups.len() > 1 && g < 100 {
// British usage: "one thousand and one", "one million and fifty".
// Only the final group gets it, and only without hundreds of its own.
text = format!("and {}", text);
}
words.push(text);
}
let body = words.join(" ");
if value < 0 {
format!("minus {}", body)
} else {
body
}
}
pub fn fune_vector(args: &[Value]) -> Value {
// Refuse what the typed signature cannot hold, with the wording TypeScript
// and Python use, rather than let the conversion below quietly change it.
if let Value::Float(f) = args[0] {
if f.fract() != 0.0 {
panic!("integerToWords needs a whole number, received {}", f);
}
}
Value::Str(integer_to_words(args[0].as_i64()))
}