surrealdb_core/sql/
bytesize.rs1use crate::err::Error;
2use crate::sql::statements::info::InfoStructure;
3use crate::sql::Value;
4use num_traits::CheckedAdd;
5use revision::revisioned;
6use serde::{Deserialize, Serialize};
7use std::fmt;
8use std::iter::{Peekable, Sum};
9use std::ops;
10use std::str::{Chars, FromStr};
11
12use super::value::{TryAdd, TrySub};
13use super::Strand;
14
15#[revisioned(revision = 1)]
16#[derive(
17 Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash, Ord,
18)]
19#[serde(rename = "$surrealdb::private::sql::Bytesize")]
20#[non_exhaustive]
21pub struct Bytesize(pub u64);
22
23const KIB: u64 = 1024;
24const MIB: u64 = KIB * 1024;
25const GIB: u64 = MIB * 1024;
26const TIB: u64 = GIB * 1024;
27const PIB: u64 = TIB * 1024;
28
29impl FromStr for Bytesize {
30 type Err = ();
31 fn from_str(s: &str) -> Result<Self, Self::Err> {
32 Self::try_from(s)
33 }
34}
35
36impl TryFrom<String> for Bytesize {
37 type Error = ();
38 fn try_from(v: String) -> Result<Self, Self::Error> {
39 Self::try_from(v.as_str())
40 }
41}
42
43impl TryFrom<Strand> for Bytesize {
44 type Error = ();
45 fn try_from(v: Strand) -> Result<Self, Self::Error> {
46 Self::try_from(v.as_str())
47 }
48}
49
50impl TryFrom<&str> for Bytesize {
51 type Error = ();
52 fn try_from(v: &str) -> Result<Self, Self::Error> {
53 match Bytesize::parse(v) {
54 Ok(v) => Ok(v),
55 _ => Err(()),
56 }
57 }
58}
59
60impl Bytesize {
61 pub const ZERO: Bytesize = Bytesize(0);
62 pub const MAX: Bytesize = Bytesize(u64::MAX);
63
64 pub fn new(b: u64) -> Self {
65 Bytesize(b)
66 }
67
68 pub fn parse(input: impl Into<String>) -> Result<Self, Error> {
69 let input = input.into().trim().to_lowercase();
70 if input.is_empty() {
71 return Err(Error::InvalidBytesize);
72 }
73
74 let mut chars: Peekable<Chars> = input.chars().peekable();
75 let mut total = Bytesize::new(0);
76
77 while chars.peek().is_some() {
78 let mut number = String::new();
80 while let Some(&c) = chars.peek() {
81 if !c.is_ascii_digit() {
82 break;
83 }
84 number.push(
85 chars
86 .next()
87 .ok_or(Error::Unreachable("Char was previously peekable".into()))?,
88 );
89 }
90
91 let value = number.parse::<u64>().map_err(|_| Error::InvalidBytesize)?;
92
93 let unit = chars.next().ok_or(Error::InvalidBytesize)?.to_ascii_lowercase();
95
96 if unit != 'b' {
98 match chars.next() {
99 Some('b') => (),
100 _ => return Err(Error::InvalidBytesize),
101 }
102 }
103
104 let bytesize = match unit {
105 'b' => Bytesize::b(value),
106 'k' => Bytesize::kb(value),
107 'm' => Bytesize::mb(value),
108 'g' => Bytesize::gb(value),
109 't' => Bytesize::tb(value),
110 'p' => Bytesize::pb(value),
111 _ => return Err(Error::InvalidBytesize),
112 };
113
114 total = total.try_add(bytesize)?;
115 }
116
117 if total == Bytesize::new(0) {
118 return Err(Error::InvalidBytesize);
119 }
120
121 Ok(total)
122 }
123
124 pub fn b(b: u64) -> Self {
125 Bytesize(b)
126 }
127
128 pub fn kb(kb: u64) -> Self {
129 Bytesize(kb * KIB)
130 }
131
132 pub fn mb(mb: u64) -> Self {
133 Bytesize(mb * MIB)
134 }
135
136 pub fn gb(gb: u64) -> Self {
137 Bytesize(gb * GIB)
138 }
139
140 pub fn tb(tb: u64) -> Self {
141 Bytesize(tb * TIB)
142 }
143
144 pub fn pb(pb: u64) -> Self {
145 Bytesize(pb * PIB)
146 }
147}
148
149impl fmt::Display for Bytesize {
150 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151 let b = self.0;
152 let pb = b / PIB;
153 let b = b % PIB;
154 let tb = b / TIB;
155 let b = b % TIB;
156 let gb = b / GIB;
157 let b = b % GIB;
158 let mb = b / MIB;
159 let b = b % MIB;
160 let kb = b / KIB;
161 let b = b % KIB;
162
163 if pb > 0 {
164 write!(f, "{pb}pb")?;
165 }
166 if tb > 0 {
167 write!(f, "{tb}tb")?;
168 }
169 if gb > 0 {
170 write!(f, "{gb}gb")?;
171 }
172 if mb > 0 {
173 write!(f, "{mb}mb")?;
174 }
175 if kb > 0 {
176 write!(f, "{kb}kb")?;
177 }
178 if b > 0 {
179 write!(f, "{b}b")?;
180 }
181 Ok(())
182 }
183}
184
185impl ops::Add for Bytesize {
186 type Output = Self;
187 fn add(self, other: Self) -> Self {
188 match self.0.checked_add(other.0) {
190 Some(v) => Bytesize::new(v),
191 None => Bytesize::new(u64::MAX),
192 }
193 }
194}
195
196impl TryAdd for Bytesize {
197 type Output = Self;
198 fn try_add(self, other: Self) -> Result<Self, Error> {
199 self.0
200 .checked_add(other.0)
201 .ok_or_else(|| Error::ArithmeticOverflow(format!("{self} + {other}")))
202 .map(Bytesize::new)
203 }
204}
205
206impl CheckedAdd for Bytesize {
207 fn checked_add(&self, other: &Self) -> Option<Self> {
208 self.0.checked_add(other.0).map(Bytesize::new)
209 }
210}
211
212impl<'b> ops::Add<&'b Bytesize> for &Bytesize {
213 type Output = Bytesize;
214 fn add(self, other: &'b Bytesize) -> Bytesize {
215 match self.0.checked_add(other.0) {
216 Some(v) => Bytesize::new(v),
217 None => Bytesize::new(u64::MAX),
218 }
219 }
220}
221
222impl<'b> TryAdd<&'b Bytesize> for &Bytesize {
223 type Output = Bytesize;
224 fn try_add(self, other: &'b Bytesize) -> Result<Bytesize, Error> {
225 self.0
226 .checked_add(other.0)
227 .ok_or_else(|| Error::ArithmeticOverflow(format!("{self} + {other}")))
228 .map(Bytesize::new)
229 }
230}
231
232impl ops::Sub for Bytesize {
233 type Output = Self;
234 fn sub(self, other: Self) -> Self {
235 match self.0.checked_sub(other.0) {
236 Some(v) => Bytesize::new(v),
237 None => Bytesize::default(),
238 }
239 }
240}
241
242impl TrySub for Bytesize {
243 type Output = Self;
244 fn try_sub(self, other: Self) -> Result<Self, Error> {
245 self.0
246 .checked_sub(other.0)
247 .ok_or_else(|| Error::ArithmeticNegativeOverflow(format!("{self} - {other}")))
248 .map(Bytesize::new)
249 }
250}
251
252impl<'b> ops::Sub<&'b Bytesize> for &Bytesize {
253 type Output = Bytesize;
254 fn sub(self, other: &'b Bytesize) -> Bytesize {
255 match self.0.checked_sub(other.0) {
256 Some(v) => Bytesize::new(v),
257 None => Bytesize::default(),
258 }
259 }
260}
261
262impl<'b> TrySub<&'b Bytesize> for &Bytesize {
263 type Output = Bytesize;
264 fn try_sub(self, other: &'b Bytesize) -> Result<Bytesize, Error> {
265 self.0
266 .checked_sub(other.0)
267 .ok_or_else(|| Error::ArithmeticNegativeOverflow(format!("{self} - {other}")))
268 .map(Bytesize::new)
269 }
270}
271
272impl Sum<Self> for Bytesize {
273 fn sum<I>(iter: I) -> Bytesize
274 where
275 I: Iterator<Item = Self>,
276 {
277 iter.fold(Bytesize::default(), |a, b| a + b)
278 }
279}
280
281impl<'a> Sum<&'a Self> for Bytesize {
282 fn sum<I>(iter: I) -> Bytesize
283 where
284 I: Iterator<Item = &'a Self>,
285 {
286 iter.fold(Bytesize::default(), |a, b| &a + b)
287 }
288}
289
290impl InfoStructure for Bytesize {
291 fn structure(self) -> Value {
292 self.to_string().into()
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::Bytesize;
299
300 #[test]
301 fn parse_bytesize() {
302 let str = "1tb8mb2b";
303 let bytesize = Bytesize::parse(str).unwrap();
304 assert_eq!(bytesize, Bytesize::new(1_099_520_016_386));
305 assert_eq!(str, bytesize.to_string());
306 }
307}