num_format/
custom_format.rs

1use crate::strings::{
2    DecString, DecimalStr, InfString, InfinityStr, MinString, MinusSignStr, NanStr, NanString,
3    PlusSignStr, PlusString, SepString, SeparatorStr,
4};
5use crate::{CustomFormatBuilder, Format, Grouping, Locale};
6
7/// Type for representing your own custom formats. Implements [`Format`].
8///
9/// # Example
10/// ```rust
11/// use num_format::{Buffer, Error, CustomFormat, Grouping};
12///
13/// fn main() -> Result<(), Error> {
14///     let format = CustomFormat::builder()
15///         .grouping(Grouping::Indian)
16///         .minus_sign("🙌")
17///         .separator("😀")
18///         .build()?;
19///
20///     let mut buf = Buffer::new();
21///     buf.write_formatted(&(-1000000), &format);
22///     assert_eq!("🙌10😀00😀000", buf.as_str());
23///
24///     Ok(())
25/// }
26/// ```
27///
28/// [`Format`]: trait.Format.html
29#[derive(Clone, Debug, Eq, PartialEq, Hash)]
30#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
31pub struct CustomFormat {
32    pub(crate) dec: DecString,
33    pub(crate) grp: Grouping,
34    pub(crate) inf: InfString,
35    pub(crate) min: MinString,
36    pub(crate) nan: NanString,
37    pub(crate) plus: PlusString,
38    pub(crate) sep: SepString,
39}
40
41impl CustomFormat {
42    /// Constructs a [`CustomFormatBuilder`].
43    ///
44    /// [`CustomFormatBuilder`]: struct.CustomFormatBuilder.html
45    pub fn builder() -> CustomFormatBuilder {
46        CustomFormatBuilder::new()
47    }
48
49    /// Turns `self` into a [`CustomFormatBuilder`].
50    ///
51    /// [`CustomFormatBuilder`]: struct.CustomFormatBuilder.html
52    pub fn into_builder(self) -> CustomFormatBuilder {
53        self.into()
54    }
55
56    /// Returns this format's representation of decimal points.
57    pub fn decimal(&self) -> &str {
58        &self.dec
59    }
60
61    /// Returns this format's [`Grouping`], which governs how digits are separated (see [`Grouping`]).
62    ///
63    /// [`Grouping`]: enum.Grouping.html
64    pub fn grouping(&self) -> Grouping {
65        self.grp
66    }
67
68    /// Returns this format's representation of infinity.
69    pub fn infinity(&self) -> &str {
70        &self.inf
71    }
72
73    /// Returns this format's representation of minus signs.
74    pub fn minus_sign(&self) -> &str {
75        &self.min
76    }
77
78    /// Returns this format's representation of NaN.
79    pub fn nan(&self) -> &str {
80        &self.nan
81    }
82
83    /// Returns this format's representation of plus signs.
84    pub fn plus_sign(&self) -> &str {
85        &self.plus
86    }
87
88    /// Returns this format's representation of separators.
89    pub fn separator(&self) -> &str {
90        &self.sep
91    }
92}
93
94impl Default for CustomFormat {
95    /// Returns a `CustomFormat` with settings equal to `Locale::en`.
96    fn default() -> Self {
97        Locale::en.into()
98    }
99}
100
101impl Format for CustomFormat {
102    #[inline(always)]
103    fn decimal(&self) -> DecimalStr<'_> {
104        DecimalStr::new(self.decimal()).unwrap()
105    }
106
107    #[inline(always)]
108    fn grouping(&self) -> Grouping {
109        self.grouping()
110    }
111
112    #[inline(always)]
113    fn infinity(&self) -> InfinityStr<'_> {
114        InfinityStr::new(self.infinity()).unwrap()
115    }
116
117    #[inline(always)]
118    fn minus_sign(&self) -> MinusSignStr<'_> {
119        MinusSignStr::new(self.minus_sign()).unwrap()
120    }
121
122    #[inline(always)]
123    fn nan(&self) -> NanStr<'_> {
124        NanStr::new(self.nan()).unwrap()
125    }
126
127    #[inline(always)]
128    fn plus_sign(&self) -> PlusSignStr<'_> {
129        PlusSignStr::new(self.plus_sign()).unwrap()
130    }
131
132    #[inline(always)]
133    fn separator(&self) -> SeparatorStr<'_> {
134        SeparatorStr::new(self.separator()).unwrap()
135    }
136}
137
138impl From<Locale> for CustomFormat {
139    fn from(locale: Locale) -> Self {
140        Self {
141            dec: DecString::new(locale.decimal()).unwrap(),
142            grp: locale.grouping(),
143            inf: InfString::new(locale.infinity()).unwrap(),
144            min: MinString::new(locale.minus_sign()).unwrap(),
145            nan: NanString::new(locale.nan()).unwrap(),
146            plus: PlusString::new(locale.plus_sign()).unwrap(),
147            sep: SepString::new(locale.separator()).unwrap(),
148        }
149    }
150}
151
152#[cfg(all(feature = "with-system-locale", any(unix, windows)))]
153mod system {
154    use super::*;
155    use crate::SystemLocale;
156
157    impl From<SystemLocale> for CustomFormat {
158        fn from(locale: SystemLocale) -> Self {
159            Self {
160                dec: DecString::new(locale.decimal()).unwrap(),
161                grp: locale.grouping(),
162                inf: InfString::new(locale.infinity()).unwrap(),
163                min: MinString::new(locale.minus_sign()).unwrap(),
164                nan: NanString::new(locale.nan()).unwrap(),
165                plus: PlusString::new(locale.plus_sign()).unwrap(),
166                sep: SepString::new(locale.separator()).unwrap(),
167            }
168        }
169    }
170}
171
172#[cfg(all(test, feature = "with-serde"))]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn test_serialization() {
178        let locale = CustomFormat::builder().build().unwrap();
179        let s = serde_json::to_string(&locale).unwrap();
180        let expected =
181            r#"{"dec":".","grp":"Standard","inf":"∞","min":"-","nan":"NaN","plus":"+","sep":","}"#;
182        assert_eq!(expected, &s);
183    }
184}