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