ming_wm_lib/
utils.rs

1use std::path::PathBuf;
2use std::fs::read_dir;
3
4use crate::fonts::measure_text;
5use crate::framebuffer_types::{ Dimensions, Point };
6
7pub fn min(one: usize, two: usize) -> usize {
8  if one > two { two } else { one } 
9}
10
11pub fn random_u32(seed: u32) -> u32 {
12  let mut seed = seed;
13  seed ^= seed << 13;
14  seed ^= seed >> 17;
15  seed ^= seed >> 5;
16  seed
17}
18
19pub trait Substring {
20  fn substring(&self, start: usize, end: usize) -> &str;
21  fn remove(&self, index: usize, len: usize) -> String;
22  fn remove_last(&self) -> String;
23  fn find_substring(&self, substr: &str) -> Option<usize>;
24}
25
26impl Substring for String {
27  fn substring(&self, start: usize, end: usize) -> &str {
28    let mut byte_start = 0;
29    let mut byte_end = 0;
30    let mut chars = self.chars();
31    for i in 0..end {
32      let char_length = chars.next().unwrap().len_utf8();
33      if i < start {
34        byte_start += char_length;
35      }
36      if i == end {
37        break;
38      }
39      byte_end += char_length;
40    }
41    &self[byte_start..byte_end]
42    /*
43    let mut result = String::new();
44    let mut chars = self.chars().skip(start);
45    for _i in 0..(end - start) {
46      result += &chars.next().unwrap().to_string();
47    }
48    result
49    */
50  }
51
52  fn remove(&self, index: usize, len: usize) -> String {
53    self.substring(0, index).to_string() + self.substring(index + len, self.chars().count())
54  }
55
56  fn remove_last(&self) -> String {
57    self.substring(0, self.chars().count() - 1).to_string()
58  }
59
60  fn find_substring(&self, substr: &str) -> Option<usize> {
61    //slightly inefficient
62    let substr_len = substr.chars().count();
63    let self_len = self.chars().count();
64    if substr_len <= self_len {
65      for start in 0..=(self_len - substr_len) {
66        if self.substring(start, start + substr_len) == substr {
67          return Some(start);
68        }
69      }
70    }
71    None
72  }
73}
74
75//the tuple is first, line #, actual line
76pub fn calc_actual_lines<'a>(lines: impl Iterator<Item = &'a String>, max_chars_per_line: usize, one_extra: bool) -> Vec<(bool, usize, String)> {
77  let mut actual_lines = Vec::new();
78  for (line_num, real_line) in lines.enumerate() {
79    let mut line = real_line.to_string() + if one_extra { " " } else { "" };
80    let mut first = true;
81    loop {
82      if line.chars().count() <= max_chars_per_line {
83        actual_lines.push((first, line_num, line));
84        break;
85      } else {
86        let mut line_chars = line.chars();
87        let mut push_string = String::new();
88        for _i in 0..max_chars_per_line {
89          push_string += &line_chars.next().unwrap().to_string();
90        }
91        actual_lines.push((first, line_num, push_string));
92        line = line_chars.collect();
93      }
94      first = false;
95    }
96  }
97  actual_lines
98}
99
100/// truncate to ... if too long (uses `measure_text`)
101pub fn trunc_words(fonts: &[String], to_measure: String, horiz_spacing: Option<usize>, max_text_width: usize) -> String {
102  if measure_text(fonts, &to_measure, horiz_spacing).width > max_text_width {
103    let mut current = String::new();
104    for c in to_measure.chars() {
105      let to_measure = current.clone() + &c.to_string() + "...";
106      if measure_text(fonts, &to_measure, horiz_spacing).width > max_text_width {
107        break;
108      }
109      current += &c.to_string();
110    }
111    current + "..."
112  } else {
113    to_measure.clone()
114  }
115}
116
117pub fn concat_paths(current_path: &str, add_path: &str) -> Result<PathBuf, ()> {
118  let mut new_path = PathBuf::from(current_path);
119  //if current_path is a file, automatically uses it's parent (a directory)
120  if new_path.is_file() {
121    new_path = new_path.parent().unwrap().to_path_buf();
122  }
123  if add_path.starts_with("/") {
124    //absolute path
125    new_path = PathBuf::from(add_path);
126  } else {
127    //relative path
128    for part in add_path.split("/") {
129      if part == ".." {
130        if let Some(parent) = new_path.parent() {
131          new_path = parent.to_path_buf();
132        } else {
133          return Err(());
134        }
135      } else {
136        new_path.push(part);
137      }
138    }
139  }
140  Ok(new_path)
141}
142
143//go from seconds to minutes:seconds
144pub fn format_seconds(seconds: u64) -> String {
145  let mut m = (seconds / 60).to_string(); //automatically rounds down
146  if m.len() == 1 {
147    m = "0".to_string() + &m;
148  }
149  let mut s = (seconds % 60).to_string();
150  if s.len() == 1 {
151    s = "0".to_string() + &s;
152  }
153  m + ":" + &s
154}
155
156pub const HEX_CHARS: [char; 16] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
157
158pub fn u8_to_hex(u: u8) -> String {
159  let mut h = String::new();
160  h.push(HEX_CHARS[(u / 16) as usize]);
161  h.push(HEX_CHARS[(u % 16) as usize]);
162  h
163}
164
165pub fn hex_to_u8(c1: char, c2: char) -> u8 {
166  (HEX_CHARS.iter().position(|c| c == &c1).unwrap() * 16 + HEX_CHARS.iter().position(|c| c == &c2).unwrap()) as u8
167}
168
169pub fn is_hex(c: char) -> bool {
170  HEX_CHARS.iter().any(|hc| hc == &c)
171}
172
173pub fn point_inside(point: Point, top_left: Point, size: Dimensions) -> bool {
174  let x = point[0];
175  let y = point[1];
176  let x2 = top_left[0];
177  let y2 = top_left[1];
178  let x3 = x2 + size[0];
179  let y3 = y2 + size[1];
180  x >= x2 && y >= y2 && x <= x3 && y <= y3
181}
182
183pub fn get_rest_of_split(split: &mut dyn Iterator<Item = &str>, sep: Option<&str>) -> String {
184  let mut rest = String::new();
185  let mut n = split.next();
186  loop {
187    if n.is_none() {
188      break;
189    }
190    rest += &n.unwrap();
191    n = split.next();
192    if n.is_some() && sep.is_some() {
193      rest += sep.unwrap();
194    }
195  }
196  rest
197}
198
199pub fn path_autocomplete(current_path: &str, partial_path: &str) -> Option<String> {
200  if let Ok(new_path) = concat_paths(current_path, &partial_path) {
201    let partial_name;
202    let parent;
203    if partial_path.ends_with("/") {
204      partial_name = "".to_string();
205      parent = new_path.as_path();
206    } else {
207      //this is just silly
208      partial_name = new_path.clone().file_name().unwrap().to_os_string().to_string_lossy().to_string();
209      parent = new_path.parent().unwrap();
210    };
211    if let Ok(entries) = read_dir(parent) {
212      for entry in entries {
213        let entry_path = entry.unwrap().path();
214        let name = entry_path.file_name().unwrap().to_os_string().to_string_lossy().to_string();
215        if name.starts_with(&partial_name) {
216          let add = name[partial_name.len()..].to_string();
217          let add_len = add.len();
218          return Some(add + if entry_path.is_dir() && add_len > 0 {
219            "/"
220          } else {
221            ""
222          });
223        }
224      }
225    }
226    None
227  } else {
228    None
229  }
230}
231
232pub fn get_all_files(dir: PathBuf) -> Vec<PathBuf> {
233  let mut files = Vec::new();
234  for entry in read_dir(dir).unwrap() {
235    let path = entry.unwrap().path();
236    if path.is_dir() {
237      files.extend(get_all_files(path));
238    } else {
239      files.push(path);
240    }
241  }
242  files
243}
244