1use std::fmt::Display;
2
3use std::path::{Path, PathBuf};
4use std::process::Command;
5use std::time::{Duration, UNIX_EPOCH};
6
7use crate::paths::get_cards_path;
8use std::io::{self, ErrorKind};
9
10use std::time::SystemTime;
11
12pub fn duration_to_days(dur: &Duration) -> f32 {
13 dur.as_secs_f32() / 86400.
14}
15
16pub fn days_to_duration(days: &f32) -> Duration {
17 Duration::from_secs_f32(days * 86400.)
18}
19
20pub fn current_time() -> Duration {
21 system_time_as_unix_time(SystemTime::now())
22}
23
24pub fn system_time_as_unix_time(time: SystemTime) -> Duration {
25 time.duration_since(SystemTime::UNIX_EPOCH)
26 .expect("Time went backwards")
27}
28
29pub fn truncate_string(input: String, max_len: usize) -> String {
31 let mut graphemes = input.chars();
32 let mut result = String::new();
33
34 for _ in 0..max_len {
35 if let Some(c) = graphemes.next() {
36 result.push(c);
37 } else {
38 break;
39 }
40 }
41
42 result
43}
44
45pub mod serde_duration_as_float_secs {
46 use serde::{Deserialize, Deserializer, Serializer};
47 use std::time::Duration;
48
49 pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
50 where
51 S: Serializer,
52 {
53 let secs = duration.as_secs_f32();
54 serializer.serialize_f32(secs)
55 }
56
57 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
58 where
59 D: Deserializer<'de>,
60 {
61 let secs = f32::deserialize(deserializer)?;
62 Ok(Duration::from_secs_f32(secs))
63 }
64}
65
66pub mod serde_duration_as_secs {
67 use serde::{Deserialize, Deserializer, Serializer};
68 use std::time::Duration;
69
70 pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
71 where
72 S: Serializer,
73 {
74 let secs = duration.as_secs();
75 serializer.serialize_u64(secs)
76 }
77
78 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
79 where
80 D: Deserializer<'de>,
81 {
82 let secs = u64::deserialize(deserializer)?;
83 Ok(Duration::from_secs(secs))
84 }
85}
86
87pub fn open_file_with_vim(path: &Path) -> io::Result<()> {
88 let status = Command::new("vim").arg(path).status()?;
89
90 if status.success() {
91 Ok(())
92 } else {
93 Err(io::Error::new(
94 ErrorKind::Other,
95 "Failed to open file with vim",
96 ))
97 }
98}
99
100pub trait MenuItem: Display {
101 fn action(&self) -> Box<dyn FnMut() -> bool>;
102}
103
104pub fn view_cards_in_explorer() {
105 open_folder_in_explorer(&get_cards_path()).unwrap()
106}
107
108fn open_folder_in_explorer(path: &Path) -> std::io::Result<()> {
109 #[cfg(target_os = "windows")]
110 {
111 Command::new("explorer").arg(path).status()?;
112 }
113
114 #[cfg(target_os = "macos")]
115 {
116 Command::new("open").arg(path).status()?;
117 }
118
119 #[cfg(target_os = "linux")]
120 {
121 Command::new("xdg-open").arg(path).status()?;
122 }
123
124 Ok(())
125}
126
127pub fn within_percentage(percentage: u32) -> bool {
130 rand_int(100) < percentage
131}
132
133pub fn rand_int(max: u32) -> u32 {
134 let time = current_time();
135 (time.as_micros() ^ time.as_nanos() ^ time.as_millis()) as u32 % max
136}
137
138pub fn get_last_modified(path: PathBuf) -> Duration {
139 let metadata = std::fs::metadata(path).unwrap();
140 let modified_time = metadata.modified().unwrap();
141 let secs = modified_time
142 .duration_since(UNIX_EPOCH)
143 .map(|s| s.as_secs())
144 .unwrap();
145 Duration::from_secs(secs)
146}
147
148pub enum FileDir {
149 File(PathBuf),
150 Dir(PathBuf),
151}
152
153impl FileDir {
154 pub fn non_rec(path: PathBuf) -> Vec<Self> {
156 let mut vec = Vec::new();
157 if !path.is_dir() {
158 panic!("damn bro");
159 }
160
161 for entry in path.read_dir().unwrap() {
162 let entry = entry.unwrap();
163 let file_type = entry.file_type().unwrap();
164
165 if entry.file_name().to_str().unwrap().starts_with('_') {
166 continue;
167 }
168
169 if file_type.is_dir() {
170 vec.push(Self::Dir(entry.path()));
171 } else if file_type.is_file() {
172 vec.push(Self::File(entry.path()));
173 };
174 }
175
176 vec
177 }
178
179 pub fn dirs(path: PathBuf) -> Vec<PathBuf> {
181 Self::non_rec(path)
182 .into_iter()
183 .filter_map(|x| match x {
184 FileDir::File(_) => None,
185 FileDir::Dir(path) => Some(path),
186 })
187 .collect()
188 }
189
190 pub fn files(path: PathBuf) -> Vec<PathBuf> {
192 Self::non_rec(path)
193 .into_iter()
194 .filter_map(|x| match x {
195 FileDir::File(path) => Some(path),
196 FileDir::Dir(_) => None,
197 })
198 .collect()
199 }
200}