fm/common/
format.rs

1use std::path::Path;
2
3use crate::common::UtfWidth;
4
5/// Shorten a path to be displayed in 50 chars or less.
6/// Each element of the path is shortened if needed.
7pub struct PathShortener {
8    size: usize,
9    path_str: String,
10}
11
12impl PathShortener {
13    const MAX_PATH_ELEM_SIZE: usize = 80;
14
15    pub fn path(path: &Path) -> Option<Self> {
16        Some(Self {
17            path_str: path.to_str()?.to_owned(),
18            size: Self::MAX_PATH_ELEM_SIZE,
19        })
20    }
21
22    pub fn with_size(mut self, size: usize) -> Self {
23        self.size = size;
24        self
25    }
26
27    pub fn shorten(self) -> String {
28        if self.path_str.utf_width() < self.size {
29            return self.path_str;
30        }
31        self.shorten_long_path()
32    }
33
34    fn shorten_long_path(self) -> String {
35        let splitted_path: Vec<_> = self.path_str.split('/').collect();
36        let size_per_elem = std::cmp::max(1, self.size / (splitted_path.len() + 1)) + 1;
37        Self::elems(splitted_path, size_per_elem).join("/")
38    }
39
40    fn elems(splitted_path: Vec<&str>, size_per_elem: usize) -> Vec<&str> {
41        splitted_path
42            .iter()
43            .filter_map(|p| Self::slice_long(p, size_per_elem))
44            .collect()
45    }
46
47    fn slice_long(p: &str, size_per_elem: usize) -> Option<&str> {
48        if p.len() <= size_per_elem {
49            Some(p)
50        } else {
51            p.get(0..size_per_elem)
52        }
53    }
54}