git_prole/
path_display.rs1use std::fmt::Debug;
2use std::fmt::Display;
3use std::path::Path;
4use std::path::MAIN_SEPARATOR_STR;
5
6use camino::Utf8Path;
7use owo_colors::OwoColorize;
8use owo_colors::Stream;
9use path_absolutize::Absolutize;
10
11use crate::current_dir::current_dir_utf8;
12
13pub trait PathDisplay: Debug + AsRef<Path> {
15 fn display_path_cwd(&self) -> String {
16 current_dir_utf8()
17 .map(|cwd| self.display_path_from(cwd))
18 .unwrap_or_else(|error| {
19 tracing::debug!(
20 %error,
21 path=?self,
22 "Failed to get current working directory for displaying path"
23 );
24 display_backup(self)
25 })
26 }
27
28 fn display_path_from(&self, base: impl AsRef<Utf8Path>) -> String;
29}
30
31impl<P> PathDisplay for P
32where
33 P: AsRef<Path> + Debug,
34{
35 fn display_path_from(&self, base: impl AsRef<Utf8Path>) -> String {
36 let path = self.as_ref();
37 Utf8Path::from_path(path)
38 .and_then(|utf8path| try_display(utf8path, base))
39 .unwrap_or_else(|| display_backup(self))
40 }
41}
42
43fn display_backup(path: impl AsRef<Path>) -> String {
44 make_colorful(path.as_ref().display())
45}
46
47fn make_colorful(path: impl Display) -> String {
48 path.if_supports_color(Stream::Stdout, |text| text.cyan())
49 .to_string()
50}
51
52fn try_display(path: impl AsRef<Utf8Path> + Debug, base: impl AsRef<Utf8Path>) -> Option<String> {
53 try_display_inner(path.as_ref(), base).map(make_colorful)
54}
55
56fn try_display_inner(
57 path: impl AsRef<Utf8Path> + Debug,
58 base: impl AsRef<Utf8Path>,
59) -> Option<String> {
60 let base = base.as_ref();
61 let normal: &Path = path.as_ref().as_ref();
62 let normal = normal.absolutize_from(base).ok()?;
63 let normal = Utf8Path::from_path(&normal)?;
64
65 if let Some(home) = dirs::home_dir() {
66 if let Ok(from_home) = normal.strip_prefix(&home) {
67 return Some(format!("~{MAIN_SEPARATOR_STR}{from_home}"));
68 }
69 }
70
71 let temp_dir = std::env::temp_dir();
72 if let Ok(from_temp) = normal.strip_prefix(&temp_dir) {
73 return Some(format!("$TMPDIR{MAIN_SEPARATOR_STR}{from_temp}"));
74 }
75
76 if let Ok(canon_temp_dir) = temp_dir.canonicalize() {
79 if let Ok(from_temp) = normal.strip_prefix(&canon_temp_dir) {
80 return Some(format!("$TMPDIR{MAIN_SEPARATOR_STR}{from_temp}"));
81 }
82 }
83
84 Some(normal.to_string())
86}