1use std::{fmt::Display, path::Path};
24
25use anyhow::Result;
26use serde::{Deserialize, Serialize};
27
28#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy)]
29pub enum Size {
30 B(usize),
31 KB(f32),
32 MB(f32),
33 GB(f32),
34 TB(f32),
35 UnknownUnits(usize),
36 #[default]
37 None,
38}
39
40impl Size {
41 fn get_num(&self) -> Option<f32> {
42 match self {
43 Self::B(num) | Self::UnknownUnits(num) => Some(*num as f32),
44 Self::KB(num) | Self::MB(num) | Self::GB(num) | Self::TB(num) => Some(*num),
45 _ => None,
46 }
47 }
48
49 pub fn round(&self, base: u8) -> Option<Self> {
51 if base != 2 && base != 10 {
52 panic!("Unknown base: {base} (supported values: 2 or 10)")
53 }
54
55 let mut size = *self;
56 let num = size.get_num();
57
58 if let None = num {
59 return None;
60 }
61 let mut num = num.unwrap();
62 let div = match base {
63 2 => 1024.,
64 10 => 1000.,
65 _ => panic!("Unknown base: {base} (supported values: 2 or 10)"), };
67
68 while num >= div {
69 if let Self::TB(_) = size {
70 break;
71 } else {
72 size = match size {
73 Size::B(_) => {
74 num /= div;
75 Size::KB(num)
76 }
77 Size::KB(_) => {
78 num /= div;
79 Size::MB(num)
80 }
81 Size::MB(_) => {
82 num /= div;
83 Size::GB(num)
84 }
85 Size::GB(_) => {
86 num /= div;
87 Size::TB(num)
88 }
89 _ => size,
90 }
91 }
92 }
93 Some(size)
94 }
95
96 pub fn get_bytes10(&self) -> Option<usize> {
97 match self {
98 Self::B(b) => Some(*b),
99 Self::KB(kb) => Some((*kb * 10f32.powi(3)) as usize),
100 Self::MB(mb) => Some((*mb * 10f32.powi(6)) as usize),
101 Self::GB(gb) => Some((*gb * 10f32.powi(9)) as usize),
102 Self::TB(tb) => Some((*tb * 10f32.powi(12)) as usize),
103 _ => None,
104 }
105 }
106
107 pub fn get_bytes2(&self) -> Option<usize> {
108 match self {
109 Self::B(b) => Some(*b),
110 Self::KB(kb) => Some((*kb * 2f32.powi(10)) as usize),
111 Self::MB(mb) => Some((*mb * 2f32.powi(20)) as usize),
112 Self::GB(gb) => Some((*gb * 2f32.powi(30)) as usize),
113 Self::TB(tb) => Some((*tb * 2f32.powi(40)) as usize),
114 _ => None,
115 }
116 }
117}
118
119impl TryFrom<&str> for Size {
120 type Error = anyhow::Error;
121 fn try_from(value: &str) -> Result<Self, Self::Error> {
122 let mut items = value.split_whitespace();
123 Ok(match (items.next(), items.next()) {
124 (Some(num), Some(units)) => {
125 let num = num.parse::<f32>()?;
126 match units.to_lowercase().as_str() {
127 "kb" | "kbytes" => Self::KB(num),
128 "mb" | "mbytes" => Self::MB(num),
129 "gb" | "gbytes" => Self::GB(num),
130 "tb" | "tbytes" => Self::TB(num),
131 _ => Self::B(num as usize),
132 }
133 }
134 (Some(num), None) => {
135 let num = num.parse::<usize>()?;
136 Self::UnknownUnits(num)
137 }
138 _ => Self::None,
139 })
140 }
141}
142
143impl Display for Size {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 write!(
146 f,
147 "{}",
148 match self {
149 Size::B(n) => format!("{n} B"),
150 Size::KB(n) => format!("{n:.2} KB"),
151 Size::MB(n) => format!("{n:.2} MB"),
152 Size::GB(n) => format!("{n:.2} GB"),
153 Size::TB(n) => format!("{n:.2} TB"),
154 Size::UnknownUnits(n) => format!("{n} ??"),
155 Size::None => format!("None"),
156 }
157 )
158 }
159}
160
161pub fn read_to_string<P: AsRef<Path>>(path: P) -> Result<String> {
162 let c = std::fs::read_to_string(path)?.trim().to_string();
163 Ok(c)
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn get_bytes_test() {
172 let s1 = Size::B(1024);
173 assert_eq!(s1.get_bytes10().unwrap_or(0), 1024);
174 let s2 = Size::KB(1.);
175 assert_eq!(s2.get_bytes2().unwrap(), 1024);
176 }
177}