git_config_value/
integer.rs1use std::{borrow::Cow, convert::TryFrom, 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 (number, suffix) = s.split_at(s.len() - 1);
71 if let (Ok(value), Ok(suffix)) = (number.parse(), suffix.parse()) {
72 Ok(Self {
73 value,
74 suffix: Some(suffix),
75 })
76 } else {
77 Err(int_err(s))
78 }
79 }
80}
81
82impl TryFrom<Cow<'_, BStr>> for Integer {
83 type Error = Error;
84
85 fn try_from(c: Cow<'_, BStr>) -> Result<Self, Self::Error> {
86 Self::try_from(c.as_ref())
87 }
88}
89
90#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
94#[allow(missing_docs)]
95pub enum Suffix {
96 Kibi,
97 Mebi,
98 Gibi,
99}
100
101impl Suffix {
102 #[must_use]
104 pub const fn bitwise_offset(self) -> usize {
105 match self {
106 Self::Kibi => 10,
107 Self::Mebi => 20,
108 Self::Gibi => 30,
109 }
110 }
111}
112
113impl Display for Suffix {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 match self {
116 Self::Kibi => write!(f, "k"),
117 Self::Mebi => write!(f, "m"),
118 Self::Gibi => write!(f, "g"),
119 }
120 }
121}
122
123#[cfg(feature = "serde")]
124impl serde::Serialize for Suffix {
125 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
126 where
127 S: serde::Serializer,
128 {
129 serializer.serialize_str(match self {
130 Self::Kibi => "k",
131 Self::Mebi => "m",
132 Self::Gibi => "g",
133 })
134 }
135}
136
137impl FromStr for Suffix {
138 type Err = ();
139
140 fn from_str(s: &str) -> Result<Self, Self::Err> {
141 match s {
142 "k" | "K" => Ok(Self::Kibi),
143 "m" | "M" => Ok(Self::Mebi),
144 "g" | "G" => Ok(Self::Gibi),
145 _ => Err(()),
146 }
147 }
148}
149
150impl TryFrom<&BStr> for Suffix {
151 type Error = ();
152
153 fn try_from(s: &BStr) -> Result<Self, Self::Error> {
154 Self::from_str(std::str::from_utf8(s).map_err(|_| ())?)
155 }
156}