use crate::{
date_time_parse_context::DateTimeParseContext, date_time_print_context::DateTimePrintContext,
date_time_printer_parser::DateTimePrinterParser, sign_style::SignStyle,
temporal_field::TemporalField,
};
pub(crate) const EXCEED_POINTS: [i64; 11] = [
0,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
];
pub(crate) fn string_size(x: i64) -> i32 {
let mut x = x;
let mut d = 1;
if x >= 0 {
d = 0;
x = -x;
}
let mut p = -10;
for i in 1..19 {
if x > p {
return i + d;
}
p *= 10;
}
19 + d
}
pub trait NumberPrinterParser: DateTimePrinterParser {
fn get_field(&self) -> TemporalField;
fn get_min_width(&self) -> isize;
fn get_max_width(&self) -> isize;
fn get_sign_style(&self) -> SignStyle;
fn get_subsequent_width(&self) -> isize;
fn set_subsequent_width(&mut self, subsequent_width: isize);
}
pub trait DefaultNumberPrinterParser: NumberPrinterParser {
fn get_value(&self, context: &DateTimePrintContext, value: i64) -> i64;
fn set_value(
&self,
context: &mut DateTimeParseContext,
value: i64,
error_pos: isize,
success_pos: isize,
) -> Result<isize, String>;
fn is_fixed_width(&self, _context: &DateTimeParseContext) -> bool {
self.get_subsequent_width() == -1
|| self.get_subsequent_width() > 0
&& self.get_min_width() == self.get_max_width()
&& self.get_sign_style() == SignStyle::NotNegative
}
}
impl<T: DefaultNumberPrinterParser> DateTimePrinterParser for T {
fn format(&self, context: &mut DateTimePrintContext, buf: &mut String) -> Result<bool, String> {
let value = match context.get_value(self.get_field()) {
Some(value) => self.get_value(context, value),
None => return Ok(false),
};
let decimal_style = context.get_formatter().get_decimal_style();
let mut size = string_size(value);
if value < 0 {
size -= 1; }
if size > self.get_max_width() as i32 {
return Err(format!(
"Field {:?} cannot be printed as the value {} exceeds the maximum print width of {}",
self.get_field(),
value,
self.get_max_width()
));
}
if value >= 0 {
match self.get_sign_style() {
SignStyle::ExceedsPad => {
if self.get_min_width() < 19 && size > self.get_min_width() as i32 {
buf.push(decimal_style.get_positive_sign());
}
}
SignStyle::Always => {
buf.push(decimal_style.get_positive_sign());
}
_ => {} }
} else {
match self.get_sign_style() {
SignStyle::Normal | SignStyle::ExceedsPad | SignStyle::Always => {
buf.push(decimal_style.get_negative_sign());
}
SignStyle::NotNegative => {
return Err(format!(
"Field {:?} cannot be printed as the value {} cannot be negative according to the SignStyle",
self.get_field(),
value
));
}
_ => {} }
}
let zero_digit = decimal_style.get_zero_digit();
let zeros = self.get_min_width() as i32 - size;
if zeros > 0 {
for _ in 0..zeros {
buf.push(zero_digit);
}
}
if zero_digit == '0' && value != i64::MIN {
buf.push_str(&format!("{}", value.abs()));
} else {
let abs_value = if value == i64::MIN {
"9223372036854775808".to_string() } else {
value.abs().to_string()
};
buf.push_str(&decimal_style.convert_number_to_i18n(&abs_value)?);
}
Ok(true)
}
fn parse(
&self,
context: &mut DateTimeParseContext,
text: &str,
position: isize,
) -> Result<isize, String> {
let length = text.len() as isize;
if position == length {
return Ok(!position);
}
let text_chars: Vec<char> = text.chars().collect();
if position < 0 || position >= text_chars.len() as isize {
return Err("Index out of bounds".to_owned());
}
let sign = text_chars[position as usize]; let mut negative = false;
let mut positive = false;
let mut position = position;
if sign == context.get_decimal_style().get_positive_sign() {
if !self.get_sign_style().parse(
true,
context.is_strict(),
self.get_min_width() == self.get_max_width(),
) {
return Ok(!position);
}
positive = true;
position += 1;
} else if sign == context.get_decimal_style().get_negative_sign() {
if !self.get_sign_style().parse(
false,
context.is_strict(),
self.get_min_width() == self.get_max_width(),
) {
return Ok(!position);
}
negative = true;
position += 1;
} else if self.get_sign_style() == SignStyle::Always && context.is_strict() {
return Ok(!position);
}
let eff_min_width = if context.is_strict() || self.is_fixed_width(context) {
self.get_min_width()
} else {
1
};
let min_end_pos = position + eff_min_width;
if min_end_pos > length {
return Ok(!position);
}
let mut eff_max_width = if context.is_strict() || self.is_fixed_width(context) {
self.get_max_width()
} else {
9
} + std::cmp::max(self.get_subsequent_width(), 0);
let mut total: i64 = 0;
let mut total_big: Option<num_bigint::BigInt> = None;
let mut pos = position;
for pass in 0..2 {
let max_end_pos = std::cmp::min(pos + eff_max_width, length);
while pos < max_end_pos {
if pos >= text_chars.len() as isize {
break;
}
let ch = text_chars[pos as usize];
pos += 1;
let digit = context.get_decimal_style().convert_to_digit(ch);
if digit < 0 {
pos -= 1;
if pos < min_end_pos {
return Ok(!position); }
break;
}
if (pos - position) > 18 {
if total_big.is_none() {
total_big = Some(num_bigint::BigInt::from(total));
}
if let Some(ref mut big) = total_big {
*big = big.clone() * 10 + digit;
}
} else {
total = total * 10 + digit;
}
}
if self.get_subsequent_width() > 0 && pass == 0 {
let parse_len = pos - position;
eff_max_width =
std::cmp::max(eff_min_width, parse_len - self.get_subsequent_width());
pos = position;
total = 0;
total_big = None;
} else {
break;
}
}
if negative {
if let Some(ref mut big) = total_big {
if *big == num_bigint::BigInt::from(0) && context.is_strict() {
return Ok(!(position - 1)); }
*big = -big.clone();
} else {
if total == 0 && context.is_strict() {
return Ok(!(position - 1)); }
total = -total;
}
} else if self.get_sign_style() == SignStyle::ExceedsPad && context.is_strict() {
let parse_len = pos - position;
if positive {
if parse_len <= self.get_min_width() {
return Ok(!(position - 1)); }
} else if parse_len > self.get_min_width() {
return Ok(!position); }
}
if let Some(total_big) = total_big {
let mut total_big = total_big;
if total_big.bits() > 63 {
total_big /= 10;
pos -= 1;
}
let total_big = match i64::try_from(total_big) {
Ok(v) => v,
Err(e) => return Err(format!("Could not fix in i64, {e}")), };
return self.set_value(context, total_big, position, pos);
}
self.set_value(context, total, position, pos)
}
fn as_number_printer_parser(&mut self) -> Option<&mut dyn NumberPrinterParser> {
Some(self)
}
}
#[derive(Debug)]
pub struct BaseNumberPrinterParser {
field: TemporalField,
min_width: isize,
max_width: isize,
sign_style: SignStyle,
subsequent_width: isize,
}
impl BaseNumberPrinterParser {
pub fn new(
field: TemporalField,
min_width: isize,
max_width: isize,
sign_style: SignStyle,
) -> Self {
BaseNumberPrinterParser {
field,
min_width,
max_width,
sign_style,
subsequent_width: 0,
}
}
}
impl NumberPrinterParser for BaseNumberPrinterParser {
fn get_field(&self) -> TemporalField {
self.field
}
fn get_min_width(&self) -> isize {
self.min_width
}
fn get_max_width(&self) -> isize {
self.max_width
}
fn get_sign_style(&self) -> SignStyle {
self.sign_style
}
fn get_subsequent_width(&self) -> isize {
self.subsequent_width
}
fn set_subsequent_width(&mut self, subsequent_width: isize) {
self.subsequent_width = subsequent_width;
}
}
impl DefaultNumberPrinterParser for BaseNumberPrinterParser {
fn get_value(&self, _context: &DateTimePrintContext, value: i64) -> i64 {
value
}
fn set_value(
&self,
context: &mut DateTimeParseContext,
value: i64,
error_pos: isize,
success_pos: isize,
) -> Result<isize, String> {
context.set_parsed_field(self.field, value, error_pos, success_pos)
}
}