1//! Utility functions.
2use crate::displayed_item_tree::VisibleItemIndex;
3use camino::Utf8PathBuf;
4#[cfg(not(target_arch = "wasm32"))]
5use std::path::{Path, PathBuf};
67/// This function takes a number and converts it's digits into the range
8/// a-p. This is nice because it makes for some easily typed ids.
9/// The function first formats the number as a hex digit and then performs
10/// the mapping.
11pub fn uint_idx_to_alpha_idx(idx: VisibleItemIndex, nvariables: usize) -> String {
12// this calculates how many hex digits we need to represent nvariables
13 // unwrap because the result should always fit into usize and because
14 // we are not going to display millions of character ids.
15let width = usize::try_from(nvariables.ilog(16)).unwrap() + 1;
16format!("{:0width$x}", idx.0)
17 .chars()
18 .map(|c| match c {
19'0' => 'a',
20'1' => 'b',
21'2' => 'c',
22'3' => 'd',
23'4' => 'e',
24'5' => 'f',
25'6' => 'g',
26'7' => 'h',
27'8' => 'i',
28'9' => 'j',
29'a' => 'k',
30'b' => 'l',
31'c' => 'm',
32'd' => 'n',
33'e' => 'o',
34'f' => 'p',
35_ => '?',
36 })
37 .collect()
38}
3940/// This is the reverse function to uint_idx_to_alpha_idx.
41pub fn alpha_idx_to_uint_idx(idx: String) -> Option<VisibleItemIndex> {
42let mapped = idx
43 .chars()
44 .map(|c| match c {
45'a' => '0',
46'b' => '1',
47'c' => '2',
48'd' => '3',
49'e' => '4',
50'f' => '5',
51'g' => '6',
52'h' => '7',
53'i' => '8',
54'j' => '9',
55'k' => 'a',
56'l' => 'b',
57'm' => 'c',
58'n' => 'd',
59'o' => 'e',
60'p' => 'f',
61_ => '?',
62 })
63 .collect::<String>();
64 usize::from_str_radix(&mapped, 16)
65 .ok()
66 .map(VisibleItemIndex)
67}
6869/// This function searches upward from `start` for directories or files matching `item`. It returns
70/// a `Vec<PathBuf>` to all found instances in order of closest to furthest away. The function only
71/// searches up within subdirectories of `end`.
72#[cfg(not(target_arch = "wasm32"))]
73pub fn search_upward(
74 start: impl AsRef<Path>,
75 end: impl AsRef<Path>,
76 item: impl AsRef<Path>,
77) -> Vec<PathBuf> {
78 start
79 .as_ref()
80 .ancestors()
81 .take_while(|p| p.starts_with(end.as_ref()))
82 .map(|p| p.join(&item))
83 .filter(|p| p.try_exists().is_ok_and(std::convert::identity))
84 .collect()
85}
8687pub fn get_multi_extension_from_filename(filename: &str) -> Option<String> {
88 filename
89 .as_bytes()
90 .iter()
91 .position(|&c| c == b'.')
92 .map(|pos| {
93let iter = filename.chars().skip(pos + 1);
94 iter.collect::<String>()
95 })
96}
9798/// Get the full extension of a path, including all extensions.
99/// For example, for "foo.tar.gz", this function returns "tar.gz", and not just "gz",
100/// like path.extension() would.
101pub fn get_multi_extension(path: &Utf8PathBuf) -> Option<String> {
102// Find the first . in the path, if any. Return the rest of the path.
103if let Some(filename) = path.file_name() {
104return get_multi_extension_from_filename(filename);
105 }
106None
107}