1use crate::{
2 Filesize, FilesizeUnit, IntoValue, ShellError, Span, Value, shell_error::generic::GenericError,
3};
4use serde::{Deserialize, Serialize};
5use std::fmt;
6use std::str::FromStr;
7use thiserror::Error;
8
9pub const SUPPORTED_DURATION_UNITS: [&str; 9] =
10 ["ns", "us", "µs", "ms", "sec", "min", "hr", "day", "wk"];
11
12#[derive(Debug, Copy, Clone, PartialEq, Eq, Error)]
17pub struct ParseUnitError(());
18
19impl fmt::Display for ParseUnitError {
20 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
21 write!(fmt, "invalid file size or duration unit")
22 }
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26pub enum Unit {
27 Filesize(FilesizeUnit),
28
29 Nanosecond,
31 Microsecond,
32 Millisecond,
33 Second,
34 Minute,
35 Hour,
36 Day,
37 Week,
38}
39
40fn duration_mul_and_check(size: i64, factor: i64, span: Span) -> Result<Value, ShellError> {
42 match size.checked_mul(factor) {
43 Some(val) => Ok(Value::duration(val, span)),
44 None => Err(ShellError::Generic(GenericError::new(
45 "duration too large",
46 "duration too large",
47 span,
48 ))),
49 }
50}
51
52impl Unit {
53 pub fn build_value(self, size: i64, span: Span) -> Result<Value, ShellError> {
54 match self {
55 Unit::Filesize(unit) => {
56 if let Some(filesize) = Filesize::from_unit(size, unit) {
57 Ok(filesize.into_value(span))
58 } else {
59 Err(ShellError::Generic(GenericError::new(
60 "filesize too large",
61 "filesize too large",
62 span,
63 )))
64 }
65 }
66 Unit::Nanosecond => Ok(Value::duration(size, span)),
67 Unit::Microsecond => duration_mul_and_check(size, 1000, span),
68 Unit::Millisecond => duration_mul_and_check(size, 1000 * 1000, span),
69 Unit::Second => duration_mul_and_check(size, 1000 * 1000 * 1000, span),
70 Unit::Minute => duration_mul_and_check(size, 1000 * 1000 * 1000 * 60, span),
71 Unit::Hour => duration_mul_and_check(size, 1000 * 1000 * 1000 * 60 * 60, span),
72 Unit::Day => duration_mul_and_check(size, 1000 * 1000 * 1000 * 60 * 60 * 24, span),
73 Unit::Week => duration_mul_and_check(size, 1000 * 1000 * 1000 * 60 * 60 * 24 * 7, span),
74 }
75 }
76
77 pub const fn as_str(&self) -> &'static str {
91 match self {
92 Unit::Filesize(u) => u.as_str(),
93 Unit::Nanosecond => "ns",
94 Unit::Microsecond => "us",
95 Unit::Millisecond => "ms",
96 Unit::Second => "sec",
97 Unit::Minute => "min",
98 Unit::Hour => "hr",
99 Unit::Day => "day",
100 Unit::Week => "wk",
101 }
102 }
103}
104
105impl FromStr for Unit {
106 type Err = ParseUnitError;
107
108 fn from_str(s: &str) -> Result<Self, Self::Err> {
109 if let Ok(filesize_unit) = FilesizeUnit::from_str(s) {
110 return Ok(Unit::Filesize(filesize_unit));
111 };
112
113 match s {
114 "ns" => Ok(Unit::Nanosecond),
115 "us" | "µs" => Ok(Unit::Microsecond),
116 "ms" => Ok(Unit::Millisecond),
117 "sec" => Ok(Unit::Second),
118 "min" => Ok(Unit::Minute),
119 "hr" => Ok(Unit::Hour),
120 "day" => Ok(Unit::Day),
121 "wk" => Ok(Unit::Week),
122 _ => Err(ParseUnitError(())),
123 }
124 }
125}