gix_config_value/
integer.rs1use std::{borrow::Cow, fmt::Display, str::FromStr};
2
3use bstr::{BStr, BString};
4
5use crate::{Error, Integer};
6
7impl Integer {
8 pub fn to_decimal(&self) -> Option<i64> {
14 match self.suffix {
15 None => Some(self.value),
16 Some(suffix) => match suffix {
17 Suffix::Kibi => self.value.checked_mul(1024),
18 Suffix::Mebi => self.value.checked_mul(1024 * 1024),
19 Suffix::Gibi => self.value.checked_mul(1024 * 1024 * 1024),
20 },
21 }
22 }
23}
24
25impl Display for Integer {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 write!(f, "{}", self.value)?;
28 if let Some(suffix) = self.suffix {
29 write!(f, "{suffix}")
30 } else {
31 Ok(())
32 }
33 }
34}
35
36#[cfg(feature = "serde")]
37impl serde::Serialize for Integer {
38 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
39 where
40 S: serde::Serializer,
41 {
42 if let Some(suffix) = self.suffix {
43 serializer.serialize_i64(self.value << suffix.bitwise_offset())
44 } else {
45 serializer.serialize_i64(self.value)
46 }
47 }
48}
49
50fn int_err(input: impl Into<BString>) -> Error {
51 Error::new(
52 "Integers needs to be positive or negative numbers which may have a suffix like 1k, 42, or 50G",
53 input,
54 )
55}
56
57impl TryFrom<&BStr> for Integer {
58 type Error = Error;
59
60 fn try_from(s: &BStr) -> Result<Self, Self::Error> {
61 let s = std::str::from_utf8(s).map_err(|err| int_err(s).with_err(err))?;
62 if let Ok(value) = s.parse() {
63 return Ok(Self { value, suffix: None });
64 }
65
66 if s.len() <= 1 {
67 return Err(int_err(s));
68 }
69
70 let last_idx = s.len() - 1;
71 if !s.is_char_boundary(last_idx) {
72 return Err(int_err(s));
73 }
74
75 let (number, suffix) = s.split_at(s.len() - 1);
76 if let (Ok(value), Ok(suffix)) = (number.parse(), suffix.parse()) {
77 Ok(Self {
78 value,
79 suffix: Some(suffix),
80 })
81 } else {
82 Err(int_err(s))
83 }
84 }
85}
86
87impl TryFrom<&str> for Integer {
88 type Error = Error;
89
90 fn try_from(value: &str) -> Result<Self, Self::Error> {
91 Self::try_from(BStr::new(value))
92 }
93}
94
95impl TryFrom<Cow<'_, BStr>> for Integer {
96 type Error = Error;
97
98 fn try_from(c: Cow<'_, BStr>) -> Result<Self, Self::Error> {
99 Self::try_from(c.as_ref())
100 }
101}
102
103impl TryFrom<BString> for Integer {
104 type Error = Error;
105
106 fn try_from(value: BString) -> Result<Self, Self::Error> {
107 Self::try_from(BStr::new(&value))
108 }
109}
110
111#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
115pub enum Suffix {
116 Kibi,
118 Mebi,
120 Gibi,
122}
123
124impl Suffix {
125 #[must_use]
127 pub const fn bitwise_offset(self) -> usize {
128 match self {
129 Self::Kibi => 10,
130 Self::Mebi => 20,
131 Self::Gibi => 30,
132 }
133 }
134}
135
136impl Display for Suffix {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 match self {
139 Self::Kibi => write!(f, "k"),
140 Self::Mebi => write!(f, "m"),
141 Self::Gibi => write!(f, "g"),
142 }
143 }
144}
145
146#[cfg(feature = "serde")]
147impl serde::Serialize for Suffix {
148 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
149 where
150 S: serde::Serializer,
151 {
152 serializer.serialize_str(match self {
153 Self::Kibi => "k",
154 Self::Mebi => "m",
155 Self::Gibi => "g",
156 })
157 }
158}
159
160impl FromStr for Suffix {
161 type Err = ();
162
163 fn from_str(s: &str) -> Result<Self, Self::Err> {
164 match s {
165 "k" | "K" => Ok(Self::Kibi),
166 "m" | "M" => Ok(Self::Mebi),
167 "g" | "G" => Ok(Self::Gibi),
168 _ => Err(()),
169 }
170 }
171}
172
173impl TryFrom<&BStr> for Suffix {
174 type Error = ();
175
176 fn try_from(s: &BStr) -> Result<Self, Self::Error> {
177 Self::from_str(std::str::from_utf8(s).map_err(|_| ())?)
178 }
179}