use core::cell::Cell;
use super::decimal::{Decimal, RoundingMode};
use super::options::{DigitPlan, Grouping, NumOpts, RoundingPriority, SignDisplay};
use super::request::{DigitOptions, INTL_DIGITS, NumberOut, NumberRequest, NumberStyle, Text};
use super::{Digits, Number, Resolved};
use crate::error::FormatError;
use crate::function::FnContext;
use crate::plural::Category;
use crate::sink::{ErrorSink, Sink, SubPartSink};
#[derive(Clone)]
pub(super) struct Shown {
plan: DigitPlan,
category: Cell<u8>,
}
pub(super) fn round_to_integer(value: &mut Decimal, mode: RoundingMode, cx: &FnContext<'_>) {
if value.is_integer() {
return;
}
let text = Text::plain(value);
let request = NumberRequest {
value: text.as_str(),
style: NumberStyle::Decimal,
neutral: true,
digits: DigitOptions {
minimum_integer: 1,
fraction: Some((0, 0)),
significant: None,
priority: RoundingPriority::Auto,
increment: 1,
mode,
strip_if_integer: false,
},
sign: SignDisplay::Auto,
grouping: Grouping::Never,
ordinal: false,
};
let mut out = Text::new();
if let Some(f) = cx.host().numbers()
&& f.format(cx.locale(), &request, NumberOut::Text(&mut out))
&& let Ok(rounded) = Decimal::parse(out.as_str().as_bytes())
{
*value = rounded;
}
}
pub(super) fn check_host(cx: &FnContext<'_>, errs: &mut dyn ErrorSink) {
if cx.host().numbers().is_none() {
errs.error(FormatError::UnsupportedOperation);
}
}
pub(super) fn shown(_value: &Decimal, _scale: i16, plan: &DigitPlan, _sign: SignDisplay) -> Shown {
Shown {
plan: *plan,
category: Cell::new(0),
}
}
pub(super) fn digits(_r: &Resolved) -> Option<Digits<'_>> {
None
}
pub(super) fn plan(r: &Resolved) -> DigitPlan {
r.shown.plan
}
pub(super) fn write_neutral(n: &Number, cx: &FnContext<'_>, out: &mut dyn Sink) {
if !n.format_by_host(cx, NumberStyle::Decimal, true, NumberOut::Text(&mut *out)) {
n.exact_digits().write_neutral(out);
}
}
pub(super) fn neutral_parts(n: &Number, cx: &FnContext<'_>, out: &mut dyn SubPartSink) {
if !n.format_by_host(cx, NumberStyle::Decimal, true, NumberOut::Parts(&mut *out)) {
n.exact_digits().neutral_parts(out);
}
}
fn scaled(n: &Number, r: &Resolved) -> Text {
if r.scale == 0 {
return Text::plain(&n.value);
}
let mut v = n.value.clone();
v.shift(r.scale);
Text::plain(&v)
}
pub(super) fn write_selected(n: &Number, r: &Resolved, cx: &FnContext<'_>, out: &mut dyn Sink) {
let text = scaled(n, r);
let request = r.request(text.as_str(), NumberStyle::Decimal, true);
if !cx
.host()
.numbers()
.is_some_and(|f| f.format(cx.locale(), &request, NumberOut::Text(&mut *out)))
{
out.push_str(text.as_str());
}
}
pub(super) fn category(n: &Number, r: &Resolved, cx: &FnContext<'_>) -> Category {
let cached = r.shown.category.get();
if cached != 0 {
return Category::from_code(cached - 1);
}
let text = scaled(n, r);
let request = r.request(text.as_str(), NumberStyle::Decimal, false);
let c = cx
.host()
.numbers()
.and_then(|f| f.plural(cx.locale(), &request))
.unwrap_or(Category::Other);
r.shown.category.set(c as u8 + 1);
c
}
pub(super) fn limit_digit_sizes(o: &mut NumOpts, errs: &mut dyn ErrorSink) {
for v in [&mut o.min_int, &mut o.min_sig, &mut o.max_sig] {
if let Some(n) = v
&& *n > INTL_DIGITS
{
errs.error(FormatError::BadOption);
*n = INTL_DIGITS;
}
}
}