Skip to main content

gix_config_value/
integer.rs

1use std::{borrow::Cow, fmt::Display, str::FromStr};
2
3use bstr::{BStr, BString};
4
5use crate::{Error, Integer};
6
7impl Integer {
8    /// Canonicalize values as simple decimal numbers.
9    /// An optional suffix of k, m, or g (case-insensitive), will cause the
10    /// value to be multiplied by 1024 (k), 1048576 (m), or 1073741824 (g) respectively.
11    ///
12    /// Returns the result if there is no multiplication overflow.
13    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/// Integer suffixes that are supported by `git-config`.
112///
113/// These values are base-2 unit of measurements, not the base-10 variants.
114#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
115pub enum Suffix {
116    /// Multiply the value by 2^10.
117    Kibi,
118    /// Multiply the value by 2^20.
119    Mebi,
120    /// Multiply the value by 2^30.
121    Gibi,
122}
123
124impl Suffix {
125    /// Returns the number of bits that the suffix shifts left by.
126    #[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}