1use anyhow::{Context, Result};
2use std::str::FromStr;
3use uuid::Uuid;
4
5pub fn human_bytes(bytes: u64) -> String {
7 const UNITS: &[&str] = &["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
8 let mut value = bytes as f64;
9 let mut unit = 0;
10 while value >= 1024.0 && unit + 1 < UNITS.len() {
11 value /= 1024.0;
12 unit += 1;
13 }
14 if unit == 0 {
15 format!("{bytes}B")
16 } else {
17 format!("{value:.2}{}", UNITS[unit])
18 }
19}
20
21pub fn parse_size_with_suffix(s: &str) -> Result<u64> {
23 let (num_str, suffix) = match s.find(|c: char| c.is_alphabetic()) {
24 Some(i) => (&s[..i], &s[i..]),
25 None => (s, ""),
26 };
27 let n: u64 = num_str
28 .parse()
29 .with_context(|| format!("invalid size number: '{num_str}'"))?;
30 let multiplier: u64 = match suffix.to_uppercase().as_str() {
31 "" => 1,
32 "K" => 1024,
33 "M" => 1024 * 1024,
34 "G" => 1024 * 1024 * 1024,
35 "T" => 1024u64.pow(4),
36 "P" => 1024u64.pow(5),
37 "E" => 1024u64.pow(6),
38 _ => anyhow::bail!("unknown size suffix: '{suffix}'"),
39 };
40 n.checked_mul(multiplier)
41 .ok_or_else(|| anyhow::anyhow!("size overflow: '{s}'"))
42}
43
44#[derive(Debug, Clone, Copy)]
49pub struct ParsedUuid(Uuid);
50
51impl std::ops::Deref for ParsedUuid {
52 type Target = Uuid;
53 fn deref(&self) -> &Uuid {
54 &self.0
55 }
56}
57
58pub fn parse_qgroupid(s: &str) -> anyhow::Result<u64> {
63 let (level_str, id_str) = s
64 .split_once('/')
65 .ok_or_else(|| anyhow::anyhow!("invalid qgroup ID '{}': expected <level>/<id>", s))?;
66 let level: u64 = level_str
67 .parse()
68 .map_err(|_| anyhow::anyhow!("invalid qgroup level '{}' in '{}'", level_str, s))?;
69 let subvolid: u64 = id_str
70 .parse()
71 .map_err(|_| anyhow::anyhow!("invalid qgroup subvolid '{}' in '{}'", id_str, s))?;
72 Ok((level << 48) | subvolid)
73}
74
75impl FromStr for ParsedUuid {
76 type Err = String;
77
78 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
79 match s {
80 "clear" => Ok(Self(Uuid::nil())),
81 "random" => Ok(Self(Uuid::new_v4())),
82 "time" => Ok(Self(Uuid::now_v7())),
83 _ => Uuid::parse_str(s)
84 .map(Self)
85 .map_err(|e| format!("invalid UUID: {e}")),
86 }
87 }
88}