use std::fmt::{Display, Formatter};
use chrono::NaiveDateTime;
use time::Duration;
use crate::attrmap::{AttrMap, AttrMapType};
use crate::sealed::Sealed;
use crate::style::{StyleMap, StyleOrigin, StyleUse, TextAttr};
use crate::ValueType;
#[derive(Debug)]
pub enum ValueFormatError {
Format(String),
NaN,
}
impl Display for ValueFormatError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
ValueFormatError::Format(s) => write!(f, "{}", s)?,
ValueFormatError::NaN => write!(f, "Digit expected")?,
}
Ok(())
}
}
impl std::error::Error for ValueFormatError {}
#[derive(Debug, Clone, Default)]
pub struct ValueFormat {
name: String,
country: Option<String>,
language: Option<String>,
script: Option<String>,
v_type: ValueType,
origin: StyleOrigin,
styleuse: StyleUse,
attr: AttrMapType,
text_attr: TextAttr,
parts: Vec<FormatPart>,
stylemaps: Option<Vec<StyleMap>>,
}
impl Sealed for ValueFormat {}
impl AttrMap for ValueFormat {
fn attr_map(&self) -> &AttrMapType {
&self.attr
}
fn attr_map_mut(&mut self) -> &mut AttrMapType {
&mut self.attr
}
}
impl ValueFormat {
pub fn new() -> Self {
ValueFormat {
name: String::from(""),
country: None,
language: None,
script: None,
v_type: ValueType::Text,
origin: Default::default(),
styleuse: Default::default(),
attr: None,
text_attr: Default::default(),
parts: Default::default(),
stylemaps: None,
}
}
pub fn new_with_name<S: Into<String>>(name: S, value_type: ValueType) -> Self {
ValueFormat {
name: name.into(),
country: None,
language: None,
script: None,
v_type: value_type,
origin: Default::default(),
styleuse: Default::default(),
attr: None,
text_attr: Default::default(),
parts: Default::default(),
stylemaps: None,
}
}
pub fn set_name<S: Into<String>>(&mut self, name: S) {
self.name = name.into();
}
pub fn name(&self) -> &String {
&self.name
}
pub fn set_country<S: Into<String>>(&mut self, country: S) {
self.country = Some(country.into());
}
pub fn country(&self) -> Option<&String> {
self.country.as_ref()
}
pub fn set_language<S: Into<String>>(&mut self, language: S) {
self.language = Some(language.into());
}
pub fn language(&self) -> Option<&String> {
self.language.as_ref()
}
pub fn set_script<S: Into<String>>(&mut self, script: S) {
self.script = Some(script.into());
}
pub fn script(&self) -> Option<&String> {
self.script.as_ref()
}
pub fn set_value_type(&mut self, value_type: ValueType) {
self.v_type = value_type;
}
pub fn value_type(&self) -> ValueType {
self.v_type
}
pub fn set_origin(&mut self, origin: StyleOrigin) {
self.origin = origin;
}
pub fn origin(&self) -> StyleOrigin {
self.origin
}
pub fn set_styleuse(&mut self, styleuse: StyleUse) {
self.styleuse = styleuse;
}
pub fn styleuse(&self) -> StyleUse {
self.styleuse
}
pub fn text(&self) -> &TextAttr {
&self.text_attr
}
pub fn text_mut(&mut self) -> &mut TextAttr {
&mut self.text_attr
}
pub fn push_boolean(&mut self) {
self.push_part(FormatPart::new_boolean());
}
pub fn push_number(&mut self, decimal: u8, grouping: bool) {
self.push_part(FormatPart::new_number(decimal, grouping));
}
pub fn push_number_fix(&mut self, decimal: u8, grouping: bool) {
self.push_part(FormatPart::new_number_fix(decimal, grouping));
}
pub fn push_fraction(
&mut self,
denominator: u32,
min_den_digits: u8,
min_int_digits: u8,
min_num_digits: u8,
grouping: bool,
) {
self.push_part(FormatPart::new_fraction(
denominator,
min_den_digits,
min_int_digits,
min_num_digits,
grouping,
));
}
pub fn push_scientific(&mut self, dec_places: u8) {
self.push_part(FormatPart::new_scientific(dec_places));
}
pub fn push_currency<S1, S2, S3>(&mut self, country: S1, language: S2, symbol: S3)
where
S1: Into<String>,
S2: Into<String>,
S3: Into<String>,
{
self.push_part(FormatPart::new_currency(country, language, symbol));
}
pub fn push_day(&mut self, number: FormatNumberStyle) {
self.push_part(FormatPart::new_day(number));
}
pub fn push_month(&mut self, number: FormatNumberStyle) {
self.push_part(FormatPart::new_month(number));
}
pub fn push_year(&mut self, number: FormatNumberStyle) {
self.push_part(FormatPart::new_year(number));
}
pub fn push_era(&mut self, number: FormatNumberStyle, calendar: FormatCalendarStyle) {
self.push_part(FormatPart::new_era(number, calendar));
}
pub fn push_day_of_week(&mut self, number: FormatNumberStyle, calendar: FormatCalendarStyle) {
self.push_part(FormatPart::new_day_of_week(number, calendar));
}
pub fn push_week_of_year(&mut self, calendar: FormatCalendarStyle) {
self.push_part(FormatPart::new_week_of_year(calendar));
}
pub fn push_quarter(&mut self, number: FormatNumberStyle, calendar: FormatCalendarStyle) {
self.push_part(FormatPart::new_quarter(number, calendar));
}
pub fn push_hours(&mut self, number: FormatNumberStyle) {
self.push_part(FormatPart::new_hours(number));
}
pub fn push_minutes(&mut self, number: FormatNumberStyle) {
self.push_part(FormatPart::new_minutes(number));
}
pub fn push_seconds(&mut self, number: FormatNumberStyle) {
self.push_part(FormatPart::new_seconds(number));
}
pub fn push_am_pm(&mut self) {
self.push_part(FormatPart::new_am_pm());
}
pub fn push_embedded_text(&mut self, position: u8) {
self.push_part(FormatPart::new_embedded_text(position));
}
pub fn push_text<S: Into<String>>(&mut self, text: S) {
self.push_part(FormatPart::new_text(text));
}
pub fn push_text_content(&mut self) {
self.push_part(FormatPart::new_text_content());
}
pub fn push_part(&mut self, part: FormatPart) {
self.parts.push(part);
}
#[allow(clippy::collapsible_if)]
pub fn push_parts(&mut self, partvec: &mut Vec<FormatPart>) {
self.parts.append(partvec);
}
pub fn parts(&self) -> &Vec<FormatPart> {
&self.parts
}
pub fn parts_mut(&mut self) -> &mut Vec<FormatPart> {
&mut self.parts
}
pub fn push_stylemap(&mut self, stylemap: StyleMap) {
self.stylemaps
.get_or_insert_with(Vec::new)
.push(stylemap);
}
pub fn stylemaps(&self) -> Option<&Vec<StyleMap>> {
self.stylemaps.as_ref()
}
pub fn stylemaps_mut(&mut self) -> &mut Vec<StyleMap> {
self.stylemaps.get_or_insert_with(Vec::new)
}
pub fn format_boolean(&self, b: bool) -> String {
let mut buf = String::new();
for p in &self.parts {
p.format_boolean(&mut buf, b);
}
buf
}
pub fn format_float(&self, f: f64) -> String {
let mut buf = String::new();
for p in &self.parts {
p.format_float(&mut buf, f);
}
buf
}
pub fn format_str<'a, S: Into<&'a str>>(&self, s: S) -> String {
let mut buf = String::new();
let s = s.into();
for p in &self.parts {
p.format_str(&mut buf, s);
}
buf
}
pub fn format_datetime(&self, d: &NaiveDateTime) -> String {
let mut buf = String::new();
let h12 = self
.parts
.iter()
.any(|v| v.part_type == FormatPartType::AmPm);
for p in &self.parts {
p.format_datetime(&mut buf, d, h12);
}
buf
}
pub fn format_time_duration(&self, d: &Duration) -> String {
let mut buf = String::new();
for p in &self.parts {
p.format_time_duration(&mut buf, d);
}
buf
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FormatPartType {
Boolean,
Number,
Fraction,
Scientific,
CurrencySymbol,
Day,
Month,
Year,
Era,
DayOfWeek,
WeekOfYear,
Quarter,
Hours,
Minutes,
Seconds,
AmPm,
EmbeddedText,
Text,
TextContent,
}
#[derive(Debug, Clone)]
pub struct FormatPart {
part_type: FormatPartType,
attr: AttrMapType,
content: Option<String>,
}
impl Sealed for FormatPart {}
impl AttrMap for FormatPart {
fn attr_map(&self) -> &AttrMapType {
&self.attr
}
fn attr_map_mut(&mut self) -> &mut AttrMapType {
&mut self.attr
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FormatNumberStyle {
Short,
Long,
}
impl Display for FormatNumberStyle {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
FormatNumberStyle::Short => write!(f, "short"),
FormatNumberStyle::Long => write!(f, "long"),
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FormatCalendarStyle {
Gregorian,
Gengou,
ROC,
Hanja,
Hijri,
Jewish,
Buddhist,
}
impl Display for FormatCalendarStyle {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
FormatCalendarStyle::Gregorian => write!(f, "gregorian"),
FormatCalendarStyle::Gengou => write!(f, "gengou"),
FormatCalendarStyle::ROC => write!(f, "ROC"),
FormatCalendarStyle::Hanja => write!(f, "hanja"),
FormatCalendarStyle::Hijri => write!(f, "hijri"),
FormatCalendarStyle::Jewish => write!(f, "jewish"),
FormatCalendarStyle::Buddhist => write!(f, "buddhist"),
}
}
}
impl FormatPart {
pub fn new(ftype: FormatPartType) -> Self {
FormatPart {
part_type: ftype,
attr: None,
content: None,
}
}
pub fn new_with_content<S: Into<String>>(ftype: FormatPartType, content: S) -> Self {
FormatPart {
part_type: ftype,
attr: None,
content: Some(content.into()),
}
}
pub fn new_boolean() -> Self {
FormatPart::new(FormatPartType::Boolean)
}
pub fn new_number(decimal: u8, grouping: bool) -> Self {
let mut p = FormatPart::new(FormatPartType::Number);
p.set_attr("number:min-integer-digits", 1.to_string());
p.set_attr("number:decimal-places", decimal.to_string());
p.set_attr("loext:min-decimal-places", 0.to_string());
if grouping {
p.set_attr("number:grouping", String::from("true"));
}
p
}
pub fn new_number_fix(decimal: u8, grouping: bool) -> Self {
let mut p = Self::new(FormatPartType::Number);
p.set_attr("number:min-integer-digits", 1.to_string());
p.set_attr("number:decimal-places", decimal.to_string());
p.set_attr("loext:min-decimal-places", decimal.to_string());
if grouping {
p.set_attr("number:grouping", String::from("true"));
}
p
}
pub fn new_fraction(
denominator: u32,
min_den_digits: u8,
min_int_digits: u8,
min_num_digits: u8,
grouping: bool,
) -> Self {
let mut p = Self::new(FormatPartType::Fraction);
p.set_attr("number:denominator-value", denominator.to_string());
p.set_attr("number:min-denominator-digits", min_den_digits.to_string());
p.set_attr("number:min-integer-digits", min_int_digits.to_string());
p.set_attr("number:min-numerator-digits", min_num_digits.to_string());
if grouping {
p.set_attr("number:grouping", String::from("true"));
}
p
}
pub fn new_scientific(dec_places: u8) -> Self {
let mut p = Self::new(FormatPartType::Scientific);
p.set_attr("number:decimal-places", dec_places.to_string());
p
}
pub fn new_currency<S1, S2, S3>(country: S1, language: S2, symbol: S3) -> Self
where
S1: Into<String>,
S2: Into<String>,
S3: Into<String>,
{
let mut p = Self::new_with_content(FormatPartType::CurrencySymbol, symbol);
p.set_attr("number:country", country.into());
p.set_attr("number:language", language.into());
p
}
pub fn new_day(number: FormatNumberStyle) -> Self {
let mut p = Self::new(FormatPartType::Day);
p.set_attr("number:style", number.to_string());
p
}
pub fn new_month(number: FormatNumberStyle) -> Self {
let mut p = Self::new(FormatPartType::Month);
p.set_attr("number:style", number.to_string());
p
}
pub fn new_year(number: FormatNumberStyle) -> Self {
let mut p = Self::new(FormatPartType::Year);
p.set_attr("number:style", number.to_string());
p
}
pub fn new_era(number: FormatNumberStyle, calendar: FormatCalendarStyle) -> Self {
let mut p = Self::new(FormatPartType::Era);
p.set_attr("number:style", number.to_string());
p.set_attr("number:calendar", calendar.to_string());
p
}
pub fn new_day_of_week(number: FormatNumberStyle, calendar: FormatCalendarStyle) -> Self {
let mut p = Self::new(FormatPartType::DayOfWeek);
p.set_attr("number:style", number.to_string());
p.set_attr("number:calendar", calendar.to_string());
p
}
pub fn new_week_of_year(calendar: FormatCalendarStyle) -> Self {
let mut p = Self::new(FormatPartType::WeekOfYear);
p.set_attr("number:calendar", calendar.to_string());
p
}
pub fn new_quarter(number: FormatNumberStyle, calendar: FormatCalendarStyle) -> Self {
let mut p = Self::new(FormatPartType::Quarter);
p.set_attr("number:style", number.to_string());
p.set_attr("number:calendar", calendar.to_string());
p
}
pub fn new_hours(number: FormatNumberStyle) -> Self {
let mut p = Self::new(FormatPartType::Hours);
p.set_attr("number:style", number.to_string());
p
}
pub fn new_minutes(number: FormatNumberStyle) -> Self {
let mut p = Self::new(FormatPartType::Minutes);
p.set_attr("number:style", number.to_string());
p
}
pub fn new_seconds(number: FormatNumberStyle) -> Self {
let mut p = Self::new(FormatPartType::Seconds);
p.set_attr("number:style", number.to_string());
p
}
pub fn new_am_pm() -> Self {
Self::new(FormatPartType::AmPm)
}
pub fn new_embedded_text(position: u8) -> Self {
let mut p = Self::new(FormatPartType::EmbeddedText);
p.set_attr("number:position", position.to_string());
p
}
pub fn new_text<S: Into<String>>(text: S) -> Self {
Self::new_with_content(FormatPartType::Text, text)
}
pub fn new_text_content() -> Self {
Self::new(FormatPartType::TextContent)
}
pub fn set_part_type(&mut self, p_type: FormatPartType) {
self.part_type = p_type;
}
pub fn part_type(&self) -> FormatPartType {
self.part_type
}
pub fn attr_def<'a0, 'a1, S0, S1>(&'a1 self, name: S0, default: S1) -> &'a1 str
where
S0: Into<&'a0 str>,
S1: Into<&'a1 str>,
{
if let Some(v) = self.attr(name.into()) {
v
} else {
default.into()
}
}
pub fn set_content<S: Into<String>>(&mut self, content: S) {
self.content = Some(content.into());
}
pub fn content(&self) -> Option<&String> {
self.content.as_ref()
}
fn format_boolean(&self, buf: &mut String, b: bool) {
match self.part_type {
FormatPartType::Boolean => {
buf.push_str(if b { "true" } else { "false" });
}
FormatPartType::Text => {
if let Some(content) = &self.content {
buf.push_str(content)
}
}
_ => {}
}
}
fn format_float(&self, buf: &mut String, f: f64) {
match self.part_type {
FormatPartType::Number => {
let dec = self.attr_def("number:decimal-places", "0").parse::<usize>();
if let Ok(dec) = dec {
buf.push_str(&format!("{:.*}", dec, f));
}
}
FormatPartType::Scientific => {
buf.push_str(&format!("{:e}", f));
}
FormatPartType::CurrencySymbol => {
if let Some(content) = &self.content {
buf.push_str(content)
}
}
FormatPartType::Text => {
if let Some(content) = &self.content {
buf.push_str(content)
}
}
_ => {}
}
}
fn format_str(&self, buf: &mut String, s: &str) {
match self.part_type {
FormatPartType::TextContent => {
buf.push_str(s);
}
FormatPartType::Text => {
if let Some(content) = &self.content {
buf.push_str(content)
}
}
_ => {}
}
}
#[allow(clippy::collapsible_if)]
fn format_datetime(&self, buf: &mut String, d: &NaiveDateTime, h12: bool) {
match self.part_type {
FormatPartType::Day => {
let is_long = self.attr_def("number:style", "") == "long";
if is_long {
buf.push_str(&d.format("%d").to_string());
} else {
buf.push_str(&d.format("%-d").to_string());
}
}
FormatPartType::Month => {
let is_long = self.attr_def("number:style", "") == "long";
let is_text = self.attr_def("number:textual", "") == "true";
if is_text {
if is_long {
buf.push_str(&d.format("%b").to_string());
} else {
buf.push_str(&d.format("%B").to_string());
}
} else {
if is_long {
buf.push_str(&d.format("%m").to_string());
} else {
buf.push_str(&d.format("%-m").to_string());
}
}
}
FormatPartType::Year => {
let is_long = self.attr_def("number:style", "") == "long";
if is_long {
buf.push_str(&d.format("%Y").to_string());
} else {
buf.push_str(&d.format("%y").to_string());
}
}
FormatPartType::DayOfWeek => {
let is_long = self.attr_def("number:style", "") == "long";
if is_long {
buf.push_str(&d.format("%A").to_string());
} else {
buf.push_str(&d.format("%a").to_string());
}
}
FormatPartType::WeekOfYear => {
let is_long = self.attr_def("number:style", "") == "long";
if is_long {
buf.push_str(&d.format("%W").to_string());
} else {
buf.push_str(&d.format("%-W").to_string());
}
}
FormatPartType::Hours => {
let is_long = self.attr_def("number:style", "") == "long";
if !h12 {
if is_long {
buf.push_str(&d.format("%H").to_string());
} else {
buf.push_str(&d.format("%-H").to_string());
}
} else {
if is_long {
buf.push_str(&d.format("%I").to_string());
} else {
buf.push_str(&d.format("%-I").to_string());
}
}
}
FormatPartType::Minutes => {
let is_long = self.attr_def("number:style", "") == "long";
if is_long {
buf.push_str(&d.format("%M").to_string());
} else {
buf.push_str(&d.format("%-M").to_string());
}
}
FormatPartType::Seconds => {
let is_long = self.attr_def("number:style", "") == "long";
if is_long {
buf.push_str(&d.format("%S").to_string());
} else {
buf.push_str(&d.format("%-S").to_string());
}
}
FormatPartType::AmPm => {
buf.push_str(&d.format("%p").to_string());
}
FormatPartType::Text => {
if let Some(content) = &self.content {
buf.push_str(content)
}
}
_ => {}
}
}
fn format_time_duration(&self, buf: &mut String, d: &Duration) {
match self.part_type {
FormatPartType::Hours => {
buf.push_str(&d.num_hours().to_string());
}
FormatPartType::Minutes => {
buf.push_str(&(d.num_minutes() % 60).to_string());
}
FormatPartType::Seconds => {
buf.push_str(&(d.num_seconds() % 60).to_string());
}
FormatPartType::Text => {
if let Some(content) = &self.content {
buf.push_str(content)
}
}
_ => {}
}
}
}
pub fn create_boolean_format<S: Into<String>>(name: S) -> ValueFormat {
let mut v = ValueFormat::new_with_name(name.into(), ValueType::Boolean);
v.push_boolean();
v
}
pub fn create_number_format<S: Into<String>>(name: S, decimal: u8, grouping: bool) -> ValueFormat {
let mut v = ValueFormat::new_with_name(name.into(), ValueType::Number);
v.push_number(decimal, grouping);
v
}
pub fn create_number_format_fixed<S: Into<String>>(
name: S,
decimal: u8,
grouping: bool,
) -> ValueFormat {
let mut v = ValueFormat::new_with_name(name.into(), ValueType::Number);
v.push_number_fix(decimal, grouping);
v
}
pub fn create_percentage_format<S: Into<String>>(name: S, decimal: u8) -> ValueFormat {
let mut v = ValueFormat::new_with_name(name.into(), ValueType::Percentage);
v.push_number_fix(decimal, false);
v.push_text("%");
v
}
pub fn create_currency_prefix<S1, S2, S3, S4>(
name: S1,
country: S2,
language: S3,
symbol: S4,
) -> ValueFormat
where
S1: Into<String>,
S2: Into<String>,
S3: Into<String>,
S4: Into<String>,
{
let mut v = ValueFormat::new_with_name(name.into(), ValueType::Currency);
v.push_currency(country.into(), language.into(), symbol.into());
v.push_text(" ");
v.push_number_fix(2, true);
v
}
pub fn create_currency_suffix<S1, S2, S3, S4>(
name: S1,
country: S2,
language: S3,
symbol: S4,
) -> ValueFormat
where
S1: Into<String>,
S2: Into<String>,
S3: Into<String>,
S4: Into<String>,
{
let mut v = ValueFormat::new_with_name(name.into(), ValueType::Currency);
v.push_number_fix(2, true);
v.push_text(" ");
v.push_currency(country.into(), language.into(), symbol.into());
v
}
pub fn create_date_dmy_format<S: Into<String>>(name: S) -> ValueFormat {
let mut v = ValueFormat::new_with_name(name.into(), ValueType::DateTime);
v.push_day(FormatNumberStyle::Long);
v.push_text(".");
v.push_month(FormatNumberStyle::Long);
v.push_text(".");
v.push_year(FormatNumberStyle::Long);
v
}
pub fn create_date_mdy_format<S: Into<String>>(name: S) -> ValueFormat {
let mut v = ValueFormat::new_with_name(name.into(), ValueType::DateTime);
v.push_month(FormatNumberStyle::Long);
v.push_text("/");
v.push_day(FormatNumberStyle::Long);
v.push_text("/");
v.push_year(FormatNumberStyle::Long);
v
}
pub fn create_datetime_format<S: Into<String>>(name: S) -> ValueFormat {
let mut v = ValueFormat::new_with_name(name.into(), ValueType::DateTime);
v.push_day(FormatNumberStyle::Long);
v.push_text(".");
v.push_month(FormatNumberStyle::Long);
v.push_text(".");
v.push_year(FormatNumberStyle::Long);
v.push_text(" ");
v.push_hours(FormatNumberStyle::Long);
v.push_text(":");
v.push_minutes(FormatNumberStyle::Long);
v.push_text(":");
v.push_seconds(FormatNumberStyle::Long);
v
}
pub fn create_time_format<S: Into<String>>(name: S) -> ValueFormat {
let mut v = ValueFormat::new_with_name(name.into(), ValueType::TimeDuration);
v.push_hours(FormatNumberStyle::Long);
v.push_text(":");
v.push_minutes(FormatNumberStyle::Long);
v.push_text(":");
v.push_seconds(FormatNumberStyle::Long);
v
}