use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr}; use bstr::{BStr, BString}; use crate::{Error, Integer}; impl Integer { /// Canonicalize values as simple decimal numbers. /// An optional suffix of k, m, or g (case-insensitive), will cause the /// value to be multiplied by 1024 (k), 1048576 (m), or 1073741824 (g) respectively. /// /// Returns the result if there is no multiplication overflow. pub fn to_decimal(&self) -> Option<i64> { match self.suffix { None => Some(self.value), Some(suffix) => match suffix { Suffix::Kibi => self.value.checked_mul(1024), Suffix::Mebi => self.value.checked_mul(1024 * 1024), Suffix::Gibi => self.value.checked_mul(1024 * 1024 * 1024), }, } }} impl Display for Integer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.value)?; if let Some(suffix) = self.suffix { write!(f, "{suffix}") } else { Ok(()) } }} #[cfg(feature = "serde")]impl serde::Serialize for Integer { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { if let Some(suffix) = self.suffix { serializer.serialize_i64(self.value << suffix.bitwise_offset()) } else { serializer.serialize_i64(self.value) } }} fn int_err(input: impl Into<BString>) -> Error { Error::new( "Integers needs to be positive or negative numbers which may have a suffix like 1k, 42, or 50G", input, )} impl TryFrom<&BStr> for Integer { type Error = Error; fn try_from(s: &BStr) -> Result<Self, Self::Error> { let s = std::str::from_utf8(s).map_err(|err| int_err(s).with_err(err))?; if let Ok(value) = s.parse() { return Ok(Self { value, suffix: None }); } if s.len() <= 1 { return Err(int_err(s)); } let (number, suffix) = s.split_at(s.len() - 1); if let (Ok(value), Ok(suffix)) = (number.parse(), suffix.parse()) { Ok(Self { value, suffix: Some(suffix), }) } else { Err(int_err(s)) } }} impl TryFrom<Cow<'_, BStr>> for Integer { type Error = Error; fn try_from(c: Cow<'_, BStr>) -> Result<Self, Self::Error> { Self::try_from(c.as_ref()) }} /// Integer suffixes that are supported by `git-config`.////// These values are base-2 unit of measurements, not the base-10 variants.#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]#[allow(missing_docs)]pub enum Suffix { Kibi, Mebi, Gibi,} impl Suffix { /// Returns the number of bits that the suffix shifts left by. #[must_use] pub const fn bitwise_offset(self) -> usize { match self { Self::Kibi => 10, Self::Mebi => 20, Self::Gibi => 30, } }} impl Display for Suffix { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Kibi => write!(f, "k"), Self::Mebi => write!(f, "m"), Self::Gibi => write!(f, "g"), } }} #[cfg(feature = "serde")]impl serde::Serialize for Suffix { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { serializer.serialize_str(match self { Self::Kibi => "k", Self::Mebi => "m", Self::Gibi => "g", }) }} impl FromStr for Suffix { type Err = (); fn from_str(s: &str) -> Result<Self, Self::Err> { match s { "k" | "K" => Ok(Self::Kibi), "m" | "M" => Ok(Self::Mebi), "g" | "G" => Ok(Self::Gibi), _ => Err(()), } }} impl TryFrom<&BStr> for Suffix { type Error = (); fn try_from(s: &BStr) -> Result<Self, Self::Error> { Self::from_str(std::str::from_utf8(s).map_err(|_| ())?) }}
use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr};
use bstr::{BStr, BString};
use crate::{Error, Integer};
impl Integer {
/// Canonicalize values as simple decimal numbers.
/// An optional suffix of k, m, or g (case-insensitive), will cause the
/// value to be multiplied by 1024 (k), 1048576 (m), or 1073741824 (g) respectively.
///
/// Returns the result if there is no multiplication overflow.
pub fn to_decimal(&self) -> Option<i64> {
match self.suffix {
None => Some(self.value),
Some(suffix) => match suffix {
Suffix::Kibi => self.value.checked_mul(1024),
Suffix::Mebi => self.value.checked_mul(1024 * 1024),
Suffix::Gibi => self.value.checked_mul(1024 * 1024 * 1024),
},
}
}
}
impl Display for Integer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)?;
if let Some(suffix) = self.suffix {
write!(f, "{suffix}")
} else {
Ok(())
}
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Integer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if let Some(suffix) = self.suffix {
serializer.serialize_i64(self.value << suffix.bitwise_offset())
} else {
serializer.serialize_i64(self.value)
}
}
}
fn int_err(input: impl Into<BString>) -> Error {
Error::new(
"Integers needs to be positive or negative numbers which may have a suffix like 1k, 42, or 50G",
input,
)
}
impl TryFrom<&BStr> for Integer {
type Error = Error;
fn try_from(s: &BStr) -> Result<Self, Self::Error> {
let s = std::str::from_utf8(s).map_err(|err| int_err(s).with_err(err))?;
if let Ok(value) = s.parse() {
return Ok(Self { value, suffix: None });
}
if s.len() <= 1 {
return Err(int_err(s));
}
let (number, suffix) = s.split_at(s.len() - 1);
if let (Ok(value), Ok(suffix)) = (number.parse(), suffix.parse()) {
Ok(Self {
value,
suffix: Some(suffix),
})
} else {
Err(int_err(s))
}
}
}
impl TryFrom<Cow<'_, BStr>> for Integer {
type Error = Error;
fn try_from(c: Cow<'_, BStr>) -> Result<Self, Self::Error> {
Self::try_from(c.as_ref())
}
}
/// Integer suffixes that are supported by `git-config`.
///
/// These values are base-2 unit of measurements, not the base-10 variants.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[allow(missing_docs)]
pub enum Suffix {
Kibi,
Mebi,
Gibi,
}
impl Suffix {
/// Returns the number of bits that the suffix shifts left by.
#[must_use]
pub const fn bitwise_offset(self) -> usize {
match self {
Self::Kibi => 10,
Self::Mebi => 20,
Self::Gibi => 30,
}
}
}
impl Display for Suffix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Kibi => write!(f, "k"),
Self::Mebi => write!(f, "m"),
Self::Gibi => write!(f, "g"),
}
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Suffix {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(match self {
Self::Kibi => "k",
Self::Mebi => "m",
Self::Gibi => "g",
})
}
}
impl FromStr for Suffix {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"k" | "K" => Ok(Self::Kibi),
"m" | "M" => Ok(Self::Mebi),
"g" | "G" => Ok(Self::Gibi),
_ => Err(()),
}
}
}
impl TryFrom<&BStr> for Suffix {
type Error = ();
fn try_from(s: &BStr) -> Result<Self, Self::Error> {
Self::from_str(std::str::from_utf8(s).map_err(|_| ())?)
}
}