atagen

roundabout

public
Star0
Watch0
4e5ca2e2040139a56cdd8bc50993e0f82ceab09a
1
ssh://brewery@pls.lobotomise.me:33/atagen/roundabout.git

Blame: src/units.rs

6f5b84fad3efatagen2026-09-27 03:59 UTC1
use std::{fmt, str::FromStr, time::Duration};
6f5b84fad3efatagen2026-09-27 03:59 UTC2
6f5b84fad3efatagen2026-09-27 03:59 UTC3
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
6f5b84fad3efatagen2026-09-27 03:59 UTC4
6f5b84fad3efatagen2026-09-27 03:59 UTC5
const KIB: u64 = 1024;
6f5b84fad3efatagen2026-09-27 03:59 UTC6
6f5b84fad3efatagen2026-09-27 03:59 UTC7
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
6f5b84fad3efatagen2026-09-27 03:59 UTC8
pub struct Bytes(pub u64);
6f5b84fad3efatagen2026-09-27 03:59 UTC9
6f5b84fad3efatagen2026-09-27 03:59 UTC10
impl Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC11
    pub const ZERO: Self = Self(0);
6f5b84fad3efatagen2026-09-27 03:59 UTC12
6f5b84fad3efatagen2026-09-27 03:59 UTC13
    pub const fn gib(n: u64) -> Self {
6f5b84fad3efatagen2026-09-27 03:59 UTC14
        Self(n * KIB * KIB * KIB)
6f5b84fad3efatagen2026-09-27 03:59 UTC15
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC16
6f5b84fad3efatagen2026-09-27 03:59 UTC17
    pub const fn mib(n: u64) -> Self {
6f5b84fad3efatagen2026-09-27 03:59 UTC18
        Self(n * KIB * KIB)
6f5b84fad3efatagen2026-09-27 03:59 UTC19
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC20
6f5b84fad3efatagen2026-09-27 03:59 UTC21
    pub const fn saturating_sub(self, other: Self) -> Self {
6f5b84fad3efatagen2026-09-27 03:59 UTC22
        Self(self.0.saturating_sub(other.0))
6f5b84fad3efatagen2026-09-27 03:59 UTC23
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC24
}
6f5b84fad3efatagen2026-09-27 03:59 UTC25
6f5b84fad3efatagen2026-09-27 03:59 UTC26
impl std::ops::Add for Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC27
    type Output = Self;
6f5b84fad3efatagen2026-09-27 03:59 UTC28
6f5b84fad3efatagen2026-09-27 03:59 UTC29
    fn add(self, other: Self) -> Self {
6f5b84fad3efatagen2026-09-27 03:59 UTC30
        Self(self.0.saturating_add(other.0))
6f5b84fad3efatagen2026-09-27 03:59 UTC31
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC32
}
6f5b84fad3efatagen2026-09-27 03:59 UTC33
6f5b84fad3efatagen2026-09-27 03:59 UTC34
impl std::ops::AddAssign for Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC35
    fn add_assign(&mut self, other: Self) {
6f5b84fad3efatagen2026-09-27 03:59 UTC36
        *self = *self + other;
6f5b84fad3efatagen2026-09-27 03:59 UTC37
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC38
}
6f5b84fad3efatagen2026-09-27 03:59 UTC39
6f5b84fad3efatagen2026-09-27 03:59 UTC40
impl std::iter::Sum for Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC41
    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
6f5b84fad3efatagen2026-09-27 03:59 UTC42
        iter.fold(Self::ZERO, |a, b| a + b)
6f5b84fad3efatagen2026-09-27 03:59 UTC43
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC44
}
6f5b84fad3efatagen2026-09-27 03:59 UTC45
6f5b84fad3efatagen2026-09-27 03:59 UTC46
fn split_number(s: &str) -> Result<(f64, &str), &'static str> {
6f5b84fad3efatagen2026-09-27 03:59 UTC47
    let s = s.trim();
6f5b84fad3efatagen2026-09-27 03:59 UTC48
    let end = s
6f5b84fad3efatagen2026-09-27 03:59 UTC49
        .find(|c: char| !(c.is_ascii_digit() || c == '.'))
6f5b84fad3efatagen2026-09-27 03:59 UTC50
        .unwrap_or(s.len());
6f5b84fad3efatagen2026-09-27 03:59 UTC51
    let (num, suffix) = s.split_at(end);
6f5b84fad3efatagen2026-09-27 03:59 UTC52
    let num = num.parse::<f64>().map_err(|_| "expected a number")?;
6f5b84fad3efatagen2026-09-27 03:59 UTC53
    if !num.is_finite() || num < 0.0 {
6f5b84fad3efatagen2026-09-27 03:59 UTC54
        return Err("expected a non-negative number");
6f5b84fad3efatagen2026-09-27 03:59 UTC55
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC56
    Ok((num, suffix.trim()))
6f5b84fad3efatagen2026-09-27 03:59 UTC57
}
6f5b84fad3efatagen2026-09-27 03:59 UTC58
6f5b84fad3efatagen2026-09-27 03:59 UTC59
impl FromStr for Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC60
    type Err = &'static str;
6f5b84fad3efatagen2026-09-27 03:59 UTC61
6f5b84fad3efatagen2026-09-27 03:59 UTC62
    fn from_str(s: &str) -> Result<Self, Self::Err> {
6f5b84fad3efatagen2026-09-27 03:59 UTC63
        let (num, suffix) = split_number(s)?;
6f5b84fad3efatagen2026-09-27 03:59 UTC64
        let scale = match suffix.to_ascii_lowercase().as_str() {
6f5b84fad3efatagen2026-09-27 03:59 UTC65
            "" | "b" => 1,
6f5b84fad3efatagen2026-09-27 03:59 UTC66
            "k" | "kb" | "kib" => KIB,
6f5b84fad3efatagen2026-09-27 03:59 UTC67
            "m" | "mb" | "mib" => KIB.pow(2),
6f5b84fad3efatagen2026-09-27 03:59 UTC68
            "g" | "gb" | "gib" => KIB.pow(3),
6f5b84fad3efatagen2026-09-27 03:59 UTC69
            "t" | "tb" | "tib" => KIB.pow(4),
6f5b84fad3efatagen2026-09-27 03:59 UTC70
            _ => return Err("expected a size like 512M or 4GiB"),
6f5b84fad3efatagen2026-09-27 03:59 UTC71
        };
6f5b84fad3efatagen2026-09-27 03:59 UTC72
        Ok(Self((num * scale as f64) as u64))
6f5b84fad3efatagen2026-09-27 03:59 UTC73
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC74
}
6f5b84fad3efatagen2026-09-27 03:59 UTC75
6f5b84fad3efatagen2026-09-27 03:59 UTC76
impl fmt::Display for Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC77
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6f5b84fad3efatagen2026-09-27 03:59 UTC78
        const UNITS: [&str; 5] = ["B", "KiB", "MiB", "GiB", "TiB"];
6f5b84fad3efatagen2026-09-27 03:59 UTC79
        let mut value = self.0 as f64;
6f5b84fad3efatagen2026-09-27 03:59 UTC80
        let mut unit = 0;
6f5b84fad3efatagen2026-09-27 03:59 UTC81
        while value >= 1024.0 && unit < UNITS.len() - 1 {
6f5b84fad3efatagen2026-09-27 03:59 UTC82
            value /= 1024.0;
6f5b84fad3efatagen2026-09-27 03:59 UTC83
            unit += 1;
6f5b84fad3efatagen2026-09-27 03:59 UTC84
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC85
        if unit == 0 {
6f5b84fad3efatagen2026-09-27 03:59 UTC86
            write!(f, "{}B", self.0)
6f5b84fad3efatagen2026-09-27 03:59 UTC87
        } else {
6f5b84fad3efatagen2026-09-27 03:59 UTC88
            write!(f, "{value:.1}{}", UNITS[unit])
6f5b84fad3efatagen2026-09-27 03:59 UTC89
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC90
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC91
}
6f5b84fad3efatagen2026-09-27 03:59 UTC92
6f5b84fad3efatagen2026-09-27 03:59 UTC93
impl pound::FromArg for Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC94
    fn from_arg(s: &str) -> Result<Self, pound::ValueError> {
6f5b84fad3efatagen2026-09-27 03:59 UTC95
        s.parse().map_err(|e| pound::ValueError::new(s, e))
6f5b84fad3efatagen2026-09-27 03:59 UTC96
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC97
}
6f5b84fad3efatagen2026-09-27 03:59 UTC98
6f5b84fad3efatagen2026-09-27 03:59 UTC99
impl Serialize for Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC100
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
6f5b84fad3efatagen2026-09-27 03:59 UTC101
        s.serialize_u64(self.0)
6f5b84fad3efatagen2026-09-27 03:59 UTC102
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC103
}
6f5b84fad3efatagen2026-09-27 03:59 UTC104
6f5b84fad3efatagen2026-09-27 03:59 UTC105
impl<'de> Deserialize<'de> for Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC106
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
6f5b84fad3efatagen2026-09-27 03:59 UTC107
        struct V;
6f5b84fad3efatagen2026-09-27 03:59 UTC108
        impl de::Visitor<'_> for V {
6f5b84fad3efatagen2026-09-27 03:59 UTC109
            type Value = Bytes;
6f5b84fad3efatagen2026-09-27 03:59 UTC110
6f5b84fad3efatagen2026-09-27 03:59 UTC111
            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6f5b84fad3efatagen2026-09-27 03:59 UTC112
                f.write_str("a byte count or a size string like \"4GiB\"")
6f5b84fad3efatagen2026-09-27 03:59 UTC113
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC114
6f5b84fad3efatagen2026-09-27 03:59 UTC115
            fn visit_u64<E: de::Error>(self, v: u64) -> Result<Bytes, E> {
6f5b84fad3efatagen2026-09-27 03:59 UTC116
                Ok(Bytes(v))
6f5b84fad3efatagen2026-09-27 03:59 UTC117
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC118
6f5b84fad3efatagen2026-09-27 03:59 UTC119
            fn visit_i64<E: de::Error>(self, v: i64) -> Result<Bytes, E> {
6f5b84fad3efatagen2026-09-27 03:59 UTC120
                u64::try_from(v)
6f5b84fad3efatagen2026-09-27 03:59 UTC121
                    .map(Bytes)
6f5b84fad3efatagen2026-09-27 03:59 UTC122
                    .map_err(|_| E::custom("negative size"))
6f5b84fad3efatagen2026-09-27 03:59 UTC123
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC124
6f5b84fad3efatagen2026-09-27 03:59 UTC125
            fn visit_str<E: de::Error>(self, v: &str) -> Result<Bytes, E> {
6f5b84fad3efatagen2026-09-27 03:59 UTC126
                v.parse().map_err(E::custom)
6f5b84fad3efatagen2026-09-27 03:59 UTC127
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC128
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC129
        d.deserialize_any(V)
6f5b84fad3efatagen2026-09-27 03:59 UTC130
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC131
}
6f5b84fad3efatagen2026-09-27 03:59 UTC132
6f5b84fad3efatagen2026-09-27 03:59 UTC133
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
6f5b84fad3efatagen2026-09-27 03:59 UTC134
pub struct Dur(pub Duration);
6f5b84fad3efatagen2026-09-27 03:59 UTC135
6f5b84fad3efatagen2026-09-27 03:59 UTC136
impl Dur {
6f5b84fad3efatagen2026-09-27 03:59 UTC137
    pub const fn secs(n: u64) -> Self {
6f5b84fad3efatagen2026-09-27 03:59 UTC138
        Self(Duration::from_secs(n))
6f5b84fad3efatagen2026-09-27 03:59 UTC139
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC140
}
6f5b84fad3efatagen2026-09-27 03:59 UTC141
6f5b84fad3efatagen2026-09-27 03:59 UTC142
impl FromStr for Dur {
6f5b84fad3efatagen2026-09-27 03:59 UTC143
    type Err = &'static str;
6f5b84fad3efatagen2026-09-27 03:59 UTC144
6f5b84fad3efatagen2026-09-27 03:59 UTC145
    fn from_str(s: &str) -> Result<Self, Self::Err> {
6f5b84fad3efatagen2026-09-27 03:59 UTC146
        let mut rest = s.trim();
6f5b84fad3efatagen2026-09-27 03:59 UTC147
        if rest.is_empty() {
6f5b84fad3efatagen2026-09-27 03:59 UTC148
            return Err("expected a duration like 90s or 5m");
6f5b84fad3efatagen2026-09-27 03:59 UTC149
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC150
        let mut total = 0.0;
6f5b84fad3efatagen2026-09-27 03:59 UTC151
        while !rest.is_empty() {
6f5b84fad3efatagen2026-09-27 03:59 UTC152
            let (num, tail) = split_number(rest)?;
6f5b84fad3efatagen2026-09-27 03:59 UTC153
            let unit_len = tail
6f5b84fad3efatagen2026-09-27 03:59 UTC154
                .find(|c: char| c.is_ascii_digit() || c == '.')
6f5b84fad3efatagen2026-09-27 03:59 UTC155
                .unwrap_or(tail.len());
6f5b84fad3efatagen2026-09-27 03:59 UTC156
            let (unit, next) = tail.split_at(unit_len);
6f5b84fad3efatagen2026-09-27 03:59 UTC157
            let scale = match unit.trim() {
6f5b84fad3efatagen2026-09-27 03:59 UTC158
                "" | "s" | "sec" | "secs" => 1.0,
6f5b84fad3efatagen2026-09-27 03:59 UTC159
                "ms" => 0.001,
6f5b84fad3efatagen2026-09-27 03:59 UTC160
                "m" | "min" | "mins" => 60.0,
6f5b84fad3efatagen2026-09-27 03:59 UTC161
                "h" | "hr" | "hrs" => 3600.0,
6f5b84fad3efatagen2026-09-27 03:59 UTC162
                "d" | "day" | "days" => 86400.0,
6f5b84fad3efatagen2026-09-27 03:59 UTC163
                _ => return Err("expected a duration like 90s or 5m"),
6f5b84fad3efatagen2026-09-27 03:59 UTC164
            };
6f5b84fad3efatagen2026-09-27 03:59 UTC165
            total = num.mul_add(scale, total);
6f5b84fad3efatagen2026-09-27 03:59 UTC166
            rest = next.trim();
6f5b84fad3efatagen2026-09-27 03:59 UTC167
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC168
        Ok(Self(Duration::from_secs_f64(total)))
6f5b84fad3efatagen2026-09-27 03:59 UTC169
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC170
}
6f5b84fad3efatagen2026-09-27 03:59 UTC171
6f5b84fad3efatagen2026-09-27 03:59 UTC172
impl fmt::Display for Dur {
6f5b84fad3efatagen2026-09-27 03:59 UTC173
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6f5b84fad3efatagen2026-09-27 03:59 UTC174
        let s = self.0.as_secs();
6f5b84fad3efatagen2026-09-27 03:59 UTC175
        match s {
6f5b84fad3efatagen2026-09-27 03:59 UTC176
            0..60 => write!(f, "{s}s"),
6f5b84fad3efatagen2026-09-27 03:59 UTC177
            60..3600 if s.is_multiple_of(60) => write!(f, "{}m", s / 60),
6f5b84fad3efatagen2026-09-27 03:59 UTC178
            60..3600 => write!(f, "{}m{}s", s / 60, s % 60),
6f5b84fad3efatagen2026-09-27 03:59 UTC179
            _ if s.is_multiple_of(3600) => write!(f, "{}h", s / 3600),
6f5b84fad3efatagen2026-09-27 03:59 UTC180
            _ => write!(f, "{}h{}m", s / 3600, (s % 3600) / 60),
6f5b84fad3efatagen2026-09-27 03:59 UTC181
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC182
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC183
}
6f5b84fad3efatagen2026-09-27 03:59 UTC184
6f5b84fad3efatagen2026-09-27 03:59 UTC185
impl pound::FromArg for Dur {
6f5b84fad3efatagen2026-09-27 03:59 UTC186
    fn from_arg(s: &str) -> Result<Self, pound::ValueError> {
6f5b84fad3efatagen2026-09-27 03:59 UTC187
        s.parse().map_err(|e| pound::ValueError::new(s, e))
6f5b84fad3efatagen2026-09-27 03:59 UTC188
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC189
}
6f5b84fad3efatagen2026-09-27 03:59 UTC190
6f5b84fad3efatagen2026-09-27 03:59 UTC191
impl Serialize for Dur {
6f5b84fad3efatagen2026-09-27 03:59 UTC192
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
6f5b84fad3efatagen2026-09-27 03:59 UTC193
        s.serialize_u64(self.0.as_secs())
6f5b84fad3efatagen2026-09-27 03:59 UTC194
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC195
}
6f5b84fad3efatagen2026-09-27 03:59 UTC196
6f5b84fad3efatagen2026-09-27 03:59 UTC197
impl<'de> Deserialize<'de> for Dur {
6f5b84fad3efatagen2026-09-27 03:59 UTC198
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
6f5b84fad3efatagen2026-09-27 03:59 UTC199
        struct V;
6f5b84fad3efatagen2026-09-27 03:59 UTC200
        impl de::Visitor<'_> for V {
6f5b84fad3efatagen2026-09-27 03:59 UTC201
            type Value = Dur;
6f5b84fad3efatagen2026-09-27 03:59 UTC202
6f5b84fad3efatagen2026-09-27 03:59 UTC203
            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6f5b84fad3efatagen2026-09-27 03:59 UTC204
                f.write_str("seconds or a duration string like \"5m\"")
6f5b84fad3efatagen2026-09-27 03:59 UTC205
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC206
6f5b84fad3efatagen2026-09-27 03:59 UTC207
            fn visit_u64<E: de::Error>(self, v: u64) -> Result<Dur, E> {
6f5b84fad3efatagen2026-09-27 03:59 UTC208
                Ok(Dur::secs(v))
6f5b84fad3efatagen2026-09-27 03:59 UTC209
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC210
6f5b84fad3efatagen2026-09-27 03:59 UTC211
            fn visit_i64<E: de::Error>(self, v: i64) -> Result<Dur, E> {
6f5b84fad3efatagen2026-09-27 03:59 UTC212
                u64::try_from(v)
6f5b84fad3efatagen2026-09-27 03:59 UTC213
                    .map(Dur::secs)
6f5b84fad3efatagen2026-09-27 03:59 UTC214
                    .map_err(|_| E::custom("negative duration"))
6f5b84fad3efatagen2026-09-27 03:59 UTC215
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC216
6f5b84fad3efatagen2026-09-27 03:59 UTC217
            fn visit_str<E: de::Error>(self, v: &str) -> Result<Dur, E> {
6f5b84fad3efatagen2026-09-27 03:59 UTC218
                v.parse().map_err(E::custom)
6f5b84fad3efatagen2026-09-27 03:59 UTC219
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC220
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC221
        d.deserialize_any(V)
6f5b84fad3efatagen2026-09-27 03:59 UTC222
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC223
}
6f5b84fad3efatagen2026-09-27 03:59 UTC224
6f5b84fad3efatagen2026-09-27 03:59 UTC225
#[derive(Clone, Copy, Debug, PartialEq)]
6f5b84fad3efatagen2026-09-27 03:59 UTC226
pub enum Amount<T> {
6f5b84fad3efatagen2026-09-27 03:59 UTC227
    Abs(T),
6f5b84fad3efatagen2026-09-27 03:59 UTC228
    Percent(f64),
6f5b84fad3efatagen2026-09-27 03:59 UTC229
}
6f5b84fad3efatagen2026-09-27 03:59 UTC230
6f5b84fad3efatagen2026-09-27 03:59 UTC231
impl Amount<Bytes> {
6f5b84fad3efatagen2026-09-27 03:59 UTC232
    pub fn resolve(self, total: Bytes) -> Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC233
        match self {
6f5b84fad3efatagen2026-09-27 03:59 UTC234
            Self::Abs(b) => b,
6f5b84fad3efatagen2026-09-27 03:59 UTC235
            Self::Percent(p) => Bytes((total.0 as f64 * p / 100.0) as u64),
6f5b84fad3efatagen2026-09-27 03:59 UTC236
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC237
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC238
}
6f5b84fad3efatagen2026-09-27 03:59 UTC239
6f5b84fad3efatagen2026-09-27 03:59 UTC240
impl Amount<u32> {
6f5b84fad3efatagen2026-09-27 03:59 UTC241
    pub fn resolve(self, total: u32) -> u32 {
6f5b84fad3efatagen2026-09-27 03:59 UTC242
        match self {
6f5b84fad3efatagen2026-09-27 03:59 UTC243
            Self::Abs(n) => n,
6f5b84fad3efatagen2026-09-27 03:59 UTC244
            Self::Percent(p) => ((f64::from(total) * p / 100.0).round() as u32).max(1),
6f5b84fad3efatagen2026-09-27 03:59 UTC245
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC246
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC247
}
6f5b84fad3efatagen2026-09-27 03:59 UTC248
6f5b84fad3efatagen2026-09-27 03:59 UTC249
impl<T: FromStr> FromStr for Amount<T> {
6f5b84fad3efatagen2026-09-27 03:59 UTC250
    type Err = &'static str;
6f5b84fad3efatagen2026-09-27 03:59 UTC251
6f5b84fad3efatagen2026-09-27 03:59 UTC252
    fn from_str(s: &str) -> Result<Self, Self::Err> {
6f5b84fad3efatagen2026-09-27 03:59 UTC253
        if let Some(p) = s.trim().strip_suffix('%') {
6f5b84fad3efatagen2026-09-27 03:59 UTC254
            let p = p
6f5b84fad3efatagen2026-09-27 03:59 UTC255
                .trim()
6f5b84fad3efatagen2026-09-27 03:59 UTC256
                .parse::<f64>()
6f5b84fad3efatagen2026-09-27 03:59 UTC257
                .map_err(|_| "expected a percentage")?;
6f5b84fad3efatagen2026-09-27 03:59 UTC258
            if !(0.0..=100.0).contains(&p) {
6f5b84fad3efatagen2026-09-27 03:59 UTC259
                return Err("percentage must be between 0 and 100");
6f5b84fad3efatagen2026-09-27 03:59 UTC260
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC261
            Ok(Self::Percent(p))
6f5b84fad3efatagen2026-09-27 03:59 UTC262
        } else {
6f5b84fad3efatagen2026-09-27 03:59 UTC263
            s.trim()
6f5b84fad3efatagen2026-09-27 03:59 UTC264
                .parse()
6f5b84fad3efatagen2026-09-27 03:59 UTC265
                .map(Self::Abs)
6f5b84fad3efatagen2026-09-27 03:59 UTC266
                .map_err(|_| "expected an amount or a percentage")
6f5b84fad3efatagen2026-09-27 03:59 UTC267
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC268
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC269
}
6f5b84fad3efatagen2026-09-27 03:59 UTC270
6f5b84fad3efatagen2026-09-27 03:59 UTC271
impl<T: FromStr> pound::FromArg for Amount<T> {
6f5b84fad3efatagen2026-09-27 03:59 UTC272
    fn from_arg(s: &str) -> Result<Self, pound::ValueError> {
6f5b84fad3efatagen2026-09-27 03:59 UTC273
        s.parse().map_err(|e| pound::ValueError::new(s, e))
6f5b84fad3efatagen2026-09-27 03:59 UTC274
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC275
}
6f5b84fad3efatagen2026-09-27 03:59 UTC276
6f5b84fad3efatagen2026-09-27 03:59 UTC277
impl<'de, T: FromStr> Deserialize<'de> for Amount<T> {
6f5b84fad3efatagen2026-09-27 03:59 UTC278
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
6f5b84fad3efatagen2026-09-27 03:59 UTC279
        struct V<T>(std::marker::PhantomData<T>);
6f5b84fad3efatagen2026-09-27 03:59 UTC280
        impl<T: FromStr> de::Visitor<'_> for V<T> {
6f5b84fad3efatagen2026-09-27 03:59 UTC281
            type Value = Amount<T>;
6f5b84fad3efatagen2026-09-27 03:59 UTC282
6f5b84fad3efatagen2026-09-27 03:59 UTC283
            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6f5b84fad3efatagen2026-09-27 03:59 UTC284
                f.write_str("an amount or a percentage string like \"75%\"")
6f5b84fad3efatagen2026-09-27 03:59 UTC285
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC286
6f5b84fad3efatagen2026-09-27 03:59 UTC287
            fn visit_u64<E: de::Error>(self, v: u64) -> Result<Amount<T>, E> {
6f5b84fad3efatagen2026-09-27 03:59 UTC288
                self.visit_str(&v.to_string())
6f5b84fad3efatagen2026-09-27 03:59 UTC289
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC290
6f5b84fad3efatagen2026-09-27 03:59 UTC291
            fn visit_i64<E: de::Error>(self, v: i64) -> Result<Amount<T>, E> {
6f5b84fad3efatagen2026-09-27 03:59 UTC292
                self.visit_str(&v.to_string())
6f5b84fad3efatagen2026-09-27 03:59 UTC293
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC294
6f5b84fad3efatagen2026-09-27 03:59 UTC295
            fn visit_str<E: de::Error>(self, v: &str) -> Result<Amount<T>, E> {
6f5b84fad3efatagen2026-09-27 03:59 UTC296
                v.parse().map_err(E::custom)
6f5b84fad3efatagen2026-09-27 03:59 UTC297
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC298
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC299
        d.deserialize_any(V(std::marker::PhantomData))
6f5b84fad3efatagen2026-09-27 03:59 UTC300
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC301
}