use super::fraction_formater::*;
use fancy_regex::Regex;
use std::borrow::Cow;
use thousands::Separable;
pub(crate) fn format_as_number<'input>(value: &f64, format: &'input str) -> Cow<'input, str> {
lazy_static! {
static ref THOUSANDS_SEP_REGEX: Regex = Regex::new(r#"(#,#|0,0)"#).unwrap();
static ref SCALE_REGEX: Regex = Regex::new(r#"(#|0)(,+)"#).unwrap();
static ref TRAILING_COMMA_REGEX: Regex = Regex::new("(#|0),+").unwrap();
static ref FRACTION_REGEX: Regex = Regex::new(r"#?.*\?{1,2}\/\?{1,2}").unwrap();
static ref SQUARE_BRACKET_REGEX: Regex = Regex::new(r"\[[^\]]+\]").unwrap();
static ref NUMBER_REGEX: Regex = Regex::new(r"(0+)(\.?)(0*)").unwrap();
}
let mut value = value.to_string();
let mut format = format.replace(['"', '*'], "");
let use_thousands = THOUSANDS_SEP_REGEX.is_match(&format).unwrap_or(false);
if use_thousands {
format = format.replace("0,0", "00");
format = format.replace("#,#", "##");
}
let mut scale: f64 = 1f64;
if SCALE_REGEX.is_match(&format).unwrap_or(false) {
let mut matches: Vec<&str> = Vec::new();
for ite in SCALE_REGEX.captures(&format).ok().flatten().unwrap().iter() {
matches.push(ite.unwrap().as_str());
}
scale = 1000i32.pow(matches[2].len() as u32) as f64;
format = TRAILING_COMMA_REGEX.replace_all(&format, "$1").into()
}
if FRACTION_REGEX.is_match(&format).unwrap_or(false) {
if value.parse::<usize>().is_err() {
value = format_as_fraction(&value.parse::<f64>().unwrap(), &format);
}
} else {
value = (value.parse::<f64>().unwrap() / scale).to_string();
format = format.replace('#', "0");
format = format.replace('\\', "");
format = format.replace("[$-.*]", "");
format = format.trim().to_string();
let m = SQUARE_BRACKET_REGEX.replace_all(&format, "");
if NUMBER_REGEX.is_match(&m).unwrap_or(false) {
let mut item: Vec<String> = Vec::new();
for ite in NUMBER_REGEX.captures(&m).ok().flatten().unwrap().iter() {
item.push(ite.unwrap().as_str().to_string());
}
value = format_straight_numeric_value(
&value,
&format,
&item,
&use_thousands,
r"(0+)(\.?)(0*)",
);
}
}
let re = Regex::new(r"\$[^0-9]*").unwrap();
if re.find(&format).ok().flatten().is_some() {
let item: Vec<&str> = re
.captures(&format)
.ok()
.flatten()
.unwrap()
.iter()
.map(|ite| ite.unwrap().as_str())
.collect();
value = format!("{}{}", item[0], value);
}
Cow::Owned(value)
}
fn format_straight_numeric_value(
value: &str,
_format: &str,
matches: &[String],
use_thousands: &bool,
_number_regex: &str,
) -> String {
let mut value = value.to_string();
let right = matches.get(3).unwrap();
if *use_thousands {
value = value.parse::<f64>().unwrap().separate_with_commas();
}
let blocks: Vec<&str> = value.split('.').collect();
let left_value = blocks[0].to_string();
let mut right_value = match blocks.get(1) {
Some(v) => v.to_string(),
None => String::from("0"),
};
if right.is_empty() {
return left_value;
}
if right.len() != right_value.len() {
if right_value == "0" {
right_value = right.to_string();
} else if right.len() > right_value.len() {
let pow = 10i32.pow(right.len() as u32);
right_value = format!("{}", right_value.parse::<i32>().unwrap() * pow);
} else {
let mut right_value_conv: String = right_value.chars().take(right.len()).collect();
let ajst_str: String = right_value.chars().skip(right.len()).take(1).collect();
let ajst_int = ajst_str.parse::<i32>().unwrap();
if ajst_int > 4 {
right_value_conv = (right_value_conv.parse::<i32>().unwrap() + 1).to_string();
}
right_value = right_value_conv;
}
}
value = format!("{}.{}", left_value, right_value);
value
}
fn merge_complex_number_format_masks(numbers: &[String], masks: &[String]) -> Vec<String> {
let mut decimal_count = numbers[1].len();
let mut post_decimal_masks: Vec<&str> = Vec::new();
for mask in masks.iter().rev() {
post_decimal_masks.push(mask);
decimal_count -= mask.to_string().len();
if decimal_count == 0 {
break;
}
}
post_decimal_masks.reverse();
vec![masks.join("."), post_decimal_masks.join(".")]
}
fn process_complex_number_format_mask(number: &f64, mask: &str) -> String {
let mut result = number.to_string();
let re = Regex::new(r#"0+"#).unwrap();
let mut masking_blocks: Vec<(&str, usize)> = Vec::new();
let mut masking_str: Vec<&str> = Vec::new();
let mut masking_beg: Vec<usize> = Vec::new();
for ite in re.captures(mask).ok().flatten().unwrap().iter() {
masking_str.push(ite.unwrap().as_str());
}
for pos in re.captures(mask).ok().flatten().unwrap().iter() {
let beg = pos.unwrap().start();
masking_beg.push(beg);
}
for i in 0..masking_str.len() {
masking_blocks.push((masking_str.get(i).unwrap(), *masking_beg.get(i).unwrap()));
}
let mut mask = mask.to_string();
if masking_blocks.len() > 1 {
let mut number = *number;
let mut offset: usize = 0;
for (block, pos) in masking_blocks.iter().rev() {
let divisor = format!("{}{}", 1, block).parse::<f64>().unwrap();
let size = block.len();
offset = *pos;
let block_value = format!("{:0width$}", (number % divisor), width = size);
number /= divisor;
let from: String = mask.chars().skip(offset).take(size).collect();
mask = mask.replace(&from, &block_value);
}
if number > 0f64 {
let from: String = mask.chars().skip(offset).collect();
mask = mask.replace(&from, &number.to_string());
}
result = mask;
}
result
}
fn complex_number_format_mask(number: &f64, mask: &str, split_on_point: &bool) -> String {
let sign = number < &0.0;
let number = number.abs();
if *split_on_point && mask.contains('.') && number.to_string().contains('.') {
let number_str = number.to_string();
let numbers_as: Vec<&str> = number_str.split('.').collect();
let mut numbers: Vec<String> = Vec::new();
for n in numbers_as {
numbers.push(n.to_string());
}
let masks_as: Vec<&str> = mask.split('.').collect();
let mut masks: Vec<String> = Vec::new();
for mask in masks_as {
masks.push(mask.to_string());
}
if masks.len() > 2 {
masks = merge_complex_number_format_masks(&numbers, &masks);
}
let result1 =
complex_number_format_mask(&numbers[0].parse::<f64>().unwrap(), &masks[0], &false);
let result2 = complex_number_format_mask(
&numbers[1]
.chars()
.rev()
.collect::<String>()
.parse::<f64>()
.unwrap(),
&masks[1].chars().rev().collect::<String>(),
&false,
)
.chars()
.rev()
.collect::<String>();
return format!("{}{}.{}", if sign { "-" } else { "" }, result1, result2);
}
let result = process_complex_number_format_mask(&number, mask);
format!("{}{}", if sign { "-" } else { "" }, result)
}