1use chrono::DateTime;
2use log::info;
3use rand::{distributions::Alphanumeric, Rng};
4use std::cmp::Ordering;
5use std::fs;
6use std::fs::File;
7use std::io;
8use std::io::copy as ioCopy;
9use std::path::Path;
10
11#[cfg(not(test))]
12use chrono::Local;
13
14#[cfg(test)]
15use chrono::Utc;
16
17use anyhow::{anyhow, Result};
18use env_logger::Env;
19
20#[derive(Debug)]
23pub struct Version {
24 pub major: u64,
25 pub minor: u64,
26 pub patch: u64,
27}
28
29impl Version {
30 pub fn from_str(s: String) -> Result<Version> {
32 let parts = s.split(".").collect::<Vec<&str>>();
33 if parts.len() != 3 {
34 return Err(anyhow!(
35 "Invalid version detected, proper format is <major>.<minor>.<patch>: {}",
36 s
37 ));
38 }
39 let mut v = Version {
40 major: 0,
41 minor: 0,
42 patch: 0,
43 };
44 v.major = parts[0].to_string().parse::<u64>()?;
45 v.minor = parts[1].to_string().parse::<u64>()?;
46 v.patch = parts[2].to_string().parse::<u64>()?;
47 Ok(v)
48 }
49 pub fn cmp(&self, other: &Version) -> Ordering {
51 match self.major.cmp(&other.major) {
52 Ordering::Greater => Ordering::Greater,
53 Ordering::Less => Ordering::Less,
54 Ordering::Equal => match self.minor.cmp(&other.minor) {
55 Ordering::Greater => Ordering::Greater,
56 Ordering::Less => Ordering::Less,
57 Ordering::Equal => match self.patch.cmp(&other.patch) {
58 Ordering::Greater => Ordering::Greater,
59 Ordering::Less => Ordering::Less,
60 Ordering::Equal => Ordering::Equal,
61 },
62 },
63 }
64 }
65}
66
67#[cfg(not(test))]
68pub fn now() -> DateTime<Local> {
69 Local::now()
70}
71
72#[cfg(test)]
73pub fn now() -> DateTime<Utc> {
74 DateTime::from_timestamp(1733974974, 0).unwrap()
75}
76
77pub fn init_logger(debug: bool) {
79 let llevel = match debug {
80 true => "info",
81 false => "error",
82 };
83 env_logger::Builder::from_env(Env::default().default_filter_or(llevel))
84 .init();
85 info!("Logger initialized")
86}
87
88pub fn generate_random_string(len: usize) -> String {
90 return rand::thread_rng()
91 .sample_iter(&Alphanumeric)
92 .take(len)
93 .map(char::from)
94 .collect();
95}
96
97pub fn download_file(url: &str, f_name: &Path) -> Result<()> {
99 let mut response = reqwest::blocking::get(url)?;
100 let mut file = File::create(f_name)?;
101 ioCopy(&mut response, &mut file)?;
102
103 Ok(())
104}
105
106pub fn tidy_up(tmp_dir: &Path) {
108 _ = fs::remove_dir_all(tmp_dir);
109}
110
111pub fn copy_all(
114 src: impl AsRef<Path>,
115 dst: impl AsRef<Path>,
116) -> io::Result<()> {
117 fs::create_dir_all(&dst)?;
118 for entry in fs::read_dir(src)? {
119 let entry = entry?;
120 let ty = entry.file_type()?;
121 if ty.is_dir() {
122 copy_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
123 } else {
124 fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
125 }
126 }
127 Ok(())
128}
129
130#[cfg(test)]
131mod tests {
132
133 use super::*;
134
135 #[test]
136 fn test_loading_version_from_string() {
137 let mut result = Version::from_str("1.2.3".to_string());
138 assert!(result.is_ok());
139 let v = result.unwrap();
140 assert_eq!(v.major, 1);
141 assert_eq!(v.minor, 2);
142 assert_eq!(v.patch, 3);
143 result = Version::from_str("12.3".to_string());
144 assert!(result.is_err());
145 result = Version::from_str("1.2.3.4".to_string());
146 assert!(result.is_err());
147 result = Version::from_str("gibberish".to_string());
148 assert!(result.is_err());
149 result = Version::from_str("1.2.3-alpha".to_string());
150 assert!(result.is_err());
151 }
152
153 #[test]
154 fn test_comparing_versions() {
155 let mut v1 = Version::from_str("1.2.3".to_string()).unwrap();
156 let mut v2 = Version::from_str("1.2.3".to_string()).unwrap();
157 assert_eq!(v1.cmp(&v2), Ordering::Equal);
158 v1 = Version::from_str("1.2.3".to_string()).unwrap();
159 v2 = Version::from_str("2.2.3".to_string()).unwrap();
160 assert_eq!(v1.cmp(&v2), Ordering::Less);
161 v1 = Version::from_str("1.2.4".to_string()).unwrap();
162 v2 = Version::from_str("1.2.3".to_string()).unwrap();
163 assert_eq!(v1.cmp(&v2), Ordering::Greater);
164 v1 = Version::from_str("1.2.3".to_string()).unwrap();
165 v2 = Version::from_str("1.4.3".to_string()).unwrap();
166 assert_eq!(v1.cmp(&v2), Ordering::Less);
167 }
168}