use super::ErrorType;
use std::{
collections::HashMap,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use terminal_size::{terminal_size, Height, Width};
pub fn parse_duration(s: &str) -> Result<Duration, ErrorType> {
let split_at = s
.chars()
.position(|c| !c.is_ascii_digit())
.unwrap_or(s.len());
let (num, suffix) = s.split_at(split_at);
let num = if num.is_empty() {
Err(ErrorType::MalformedInstruction)?
} else {
num.parse()?
};
match suffix {
"s" => Ok(Duration::from_secs(num)),
"ms" => Ok(Duration::from_millis(num)),
"us" => Ok(Duration::from_micros(num)),
"" if num == 0 => Ok(Duration::from_secs(0)),
_ => Err(ErrorType::MalformedInstruction),
}
}
pub fn parse_loose_string(s: &str) -> Result<String, ErrorType> {
if s.starts_with('"') && s.ends_with('"') {
Ok(serde_json::from_str(s)?)
} else {
Ok(s.to_string())
}
}
pub fn get_terminal_size() -> (u16, u16) {
terminal_size().map_or((80, 24), |(Width(w), Height(h))| (w, h))
}
pub fn capture_env_vars(env_vars: Vec<String>) -> HashMap<String, String> {
let mut env_map = HashMap::new();
for env_var in env_vars {
if let Ok(value) = std::env::var(&env_var) {
env_map.insert(env_var, value);
}
}
env_map
}
pub fn timestamp() -> Result<u64, ErrorType> {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())?;
Ok(timestamp)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn duration() {
let durations = [
("1s", Duration::from_secs(1)),
("2ms", Duration::from_millis(2)),
("3us", Duration::from_micros(3)),
("0", Duration::from_secs(0)),
];
for (input, expected) in &durations {
assert_eq!(parse_duration(input).unwrap(), *expected);
}
let bad_durations = ["1", "1x", "s", ""];
for input in &bad_durations {
let err = parse_duration(input).unwrap_err();
assert!(
matches!(err, ErrorType::MalformedInstruction),
"Expected MalformedInstruction, got {err:?}"
);
}
}
#[test]
fn loose_string() {
let strings = [
("\"hello \"", "hello "),
("world", "world"),
("\" hello \\\"world \"", " hello \"world "),
];
for (input, expected) in &strings {
assert_eq!(parse_loose_string(input).unwrap(), *expected);
}
}
#[test]
fn loose_string_error() {
let strings = ["\"hello\" world\"", "\"hello\" world\" again\""];
for input in &strings {
let err = parse_loose_string(input).unwrap_err();
assert!(
matches!(err, ErrorType::Json(_)),
"Expected Json error, got {err:?}"
);
}
}
}