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
38impl Unit {
39 pub fn build_value(self, size: i64, span: Span) -> Result<Value, ShellError> {
40 match self {
41 Unit::Filesize(unit) => {
42 if let Some(filesize) = Filesize::from_unit(size, unit) {
43 Ok(filesize.into_value(span))
44 } else {
45 Err(ShellError::GenericError {
46 error: "filesize too large".into(),
47 msg: "filesize too large".into(),
48 span: Some(span),
49 help: None,
50 inner: vec![],
51 })
52 }
53 }
54 Unit::Nanosecond => Ok(Value::duration(size, span)),
55 Unit::Microsecond => Ok(Value::duration(size * 1000, span)),
56 Unit::Millisecond => Ok(Value::duration(size * 1000 * 1000, span)),
57 Unit::Second => Ok(Value::duration(size * 1000 * 1000 * 1000, span)),
58 Unit::Minute => match size.checked_mul(1000 * 1000 * 1000 * 60) {
59 Some(val) => Ok(Value::duration(val, span)),
60 None => Err(ShellError::GenericError {
61 error: "duration too large".into(),
62 msg: "duration too large".into(),
63 span: Some(span),
64 help: None,
65 inner: vec![],
66 }),
67 },
68 Unit::Hour => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60) {
69 Some(val) => Ok(Value::duration(val, span)),
70 None => Err(ShellError::GenericError {
71 error: "duration too large".into(),
72 msg: "duration too large".into(),
73 span: Some(span),
74 help: None,
75 inner: vec![],
76 }),
77 },
78 Unit::Day => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24) {
79 Some(val) => Ok(Value::duration(val, span)),
80 None => Err(ShellError::GenericError {
81 error: "duration too large".into(),
82 msg: "duration too large".into(),
83 span: Some(span),
84 help: None,
85 inner: vec![],
86 }),
87 },
88 Unit::Week => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24 * 7) {
89 Some(val) => Ok(Value::duration(val, span)),
90 None => Err(ShellError::GenericError {
91 error: "duration too large".into(),
92 msg: "duration too large".into(),
93 span: Some(span),
94 help: None,
95 inner: vec![],
96 }),
97 },
98 }
99 }
100
101 pub const fn as_str(&self) -> &'static str {
115 match self {
116 Unit::Filesize(u) => u.as_str(),
117 Unit::Nanosecond => "ns",
118 Unit::Microsecond => "us",
119 Unit::Millisecond => "ms",
120 Unit::Second => "sec",
121 Unit::Minute => "min",
122 Unit::Hour => "hr",
123 Unit::Day => "day",
124 Unit::Week => "wk",
125 }
126 }
127}
128
129impl FromStr for Unit {
130 type Err = ParseUnitError;
131
132 fn from_str(s: &str) -> Result<Self, Self::Err> {
133 if let Ok(filesize_unit) = FilesizeUnit::from_str(s) {
134 return Ok(Unit::Filesize(filesize_unit));
135 };
136
137 match s {
138 "ns" => Ok(Unit::Nanosecond),
139 "us" | "µs" => Ok(Unit::Microsecond),
140 "ms" => Ok(Unit::Millisecond),
141 "sec" => Ok(Unit::Second),
142 "min" => Ok(Unit::Minute),
143 "hr" => Ok(Unit::Hour),
144 "day" => Ok(Unit::Day),
145 "wk" => Ok(Unit::Week),
146 _ => Err(ParseUnitError(())),
147 }
148 }
149}