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