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<Cow<'_, BStr>> for Integer {
88 type Error = Error;
89
90 fn try_from(c: Cow<'_, BStr>) -> Result<Self, Self::Error> {
91 Self::try_from(c.as_ref())
92 }
93}
94
95#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
99#[allow(missing_docs)]
100pub enum Suffix {
101 Kibi,
102 Mebi,
103 Gibi,
104}
105
106impl Suffix {
107 #[must_use]
109 pub const fn bitwise_offset(self) -> usize {
110 match self {
111 Self::Kibi => 10,
112 Self::Mebi => 20,
113 Self::Gibi => 30,
114 }
115 }
116}
117
118impl Display for Suffix {
119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120 match self {
121 Self::Kibi => write!(f, "k"),
122 Self::Mebi => write!(f, "m"),
123 Self::Gibi => write!(f, "g"),
124 }
125 }
126}
127
128#[cfg(feature = "serde")]
129impl serde::Serialize for Suffix {
130 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
131 where
132 S: serde::Serializer,
133 {
134 serializer.serialize_str(match self {
135 Self::Kibi => "k",
136 Self::Mebi => "m",
137 Self::Gibi => "g",
138 })
139 }
140}
141
142impl FromStr for Suffix {
143 type Err = ();
144
145 fn from_str(s: &str) -> Result<Self, Self::Err> {
146 match s {
147 "k" | "K" => Ok(Self::Kibi),
148 "m" | "M" => Ok(Self::Mebi),
149 "g" | "G" => Ok(Self::Gibi),
150 _ => Err(()),
151 }
152 }
153}
154
155impl TryFrom<&BStr> for Suffix {
156 type Error = ();
157
158 fn try_from(s: &BStr) -> Result<Self, Self::Error> {
159 Self::from_str(std::str::from_utf8(s).map_err(|_| ())?)
160 }
161}