Skip to main content

rightkit_qa/
util.rs

1//! Shared error type and small helpers.
2use sha2::{Digest, Sha256};
3use std::fmt;
4use std::time::{Duration, SystemTime, UNIX_EPOCH};
5
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// One flat error: every failure in a QA run is a message a human reads.
9#[derive(Debug, Clone)]
10pub struct Error(pub String);
11
12impl fmt::Display for Error {
13    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14        f.write_str(&self.0)
15    }
16}
17impl std::error::Error for Error {}
18impl From<std::io::Error> for Error {
19    fn from(e: std::io::Error) -> Self {
20        Error(e.to_string())
21    }
22}
23impl From<serde_json::Error> for Error {
24    fn from(e: serde_json::Error) -> Self {
25        Error(format!("json: {e}"))
26    }
27}
28impl From<toml::de::Error> for Error {
29    fn from(e: toml::de::Error) -> Self {
30        Error(format!("toml: {e}"))
31    }
32}
33
34pub fn err<T>(msg: impl Into<String>) -> Result<T> {
35    Err(Error(msg.into()))
36}
37
38pub fn now_iso() -> String {
39    time::OffsetDateTime::now_utc()
40        .format(&time::format_description::well_known::Rfc3339)
41        .unwrap_or_default()
42}
43
44pub fn now_ms() -> u128 {
45    SystemTime::now()
46        .duration_since(UNIX_EPOCH)
47        .map(|d| d.as_millis())
48        .unwrap_or(0)
49}
50
51pub fn sha256_hex(bytes: &[u8]) -> String {
52    format!("{:x}", Sha256::digest(bytes))
53}
54
55pub fn new_id() -> String {
56    uuid::Uuid::new_v4().to_string()
57}
58
59pub fn sleep_ms(ms: u64) {
60    std::thread::sleep(Duration::from_millis(ms));
61}
62
63/// Last `max` bytes of `s`, on a char boundary.
64pub fn tail(s: &str, max: usize) -> String {
65    if s.len() <= max {
66        return s.to_string();
67    }
68    let mut start = s.len() - max;
69    while !s.is_char_boundary(start) {
70        start += 1;
71    }
72    s[start..].to_string()
73}
74
75pub fn require_text(value: &str, name: &str) -> Result<String> {
76    let v = value.trim();
77    if v.is_empty() {
78        return err(format!("{name} is required"));
79    }
80    Ok(v.to_string())
81}
82
83/// `darwin` or `windows`; QA runs on those two desktops only.
84pub fn platform() -> Result<&'static str> {
85    if cfg!(target_os = "macos") {
86        Ok("darwin")
87    } else if cfg!(target_os = "windows") {
88        Ok("windows")
89    } else {
90        err(format!(
91            "rightkit-qa supports macOS and Windows, received {}",
92            std::env::consts::OS
93        ))
94    }
95}