1use std::fmt::Display;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5use anyhow::{Context, Result};
6
7pub fn canonicalize(p: &Path) -> Result<CanonicalizedPathBuf> {
8 fs::canonicalize(p)
9 .with_context(|| format!("error opening file {}", p.to_str().unwrap()))
10 .map(CanonicalizedPathBuf)
11}
12
13#[derive(Debug, Eq, PartialEq, Hash, Clone, Default)]
14pub struct CanonicalizedPathBuf(PathBuf);
15
16impl CanonicalizedPathBuf {
17 pub fn to_str(&self) -> &str {
18 self.0
19 .to_str()
20 .expect("encountered a non-utf8 character in file path!")
21 }
22
23 pub fn join<P: AsRef<Path>>(&self, path: P) -> Self {
25 canonicalize(&self.0.join(path)).unwrap()
26 }
27
28 #[cfg(test)]
29 pub fn path_buf(self) -> PathBuf {
30 self.0
31 }
32
33 pub fn file_name(&self) -> &str {
34 self.0
35 .file_name()
36 .unwrap()
37 .to_str()
38 .expect("encountered a non-utf8 character in file path!")
39 }
40}
41
42impl Display for CanonicalizedPathBuf {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 self.0.display().fmt(f)
45 }
46}
47
48impl AsRef<Path> for CanonicalizedPathBuf {
49 fn as_ref(&self) -> &Path {
50 &self.0
51 }
52}
53
54impl From<CanonicalizedPathBuf> for PathBuf {
55 fn from(cpb: CanonicalizedPathBuf) -> Self {
56 cpb.0
57 }
58}
59
60pub fn get_dependency_path<O: AsRef<Path>>(
61 origin_path: O,
62 path: &str,
63) -> Result<CanonicalizedPathBuf> {
64 let origin_path = origin_path.as_ref();
65 let path = Path::new(path);
66 let ret = if path.is_absolute() {
67 path.to_path_buf()
68 } else if origin_path.is_dir() {
69 origin_path.join(path)
70 } else {
71 origin_path.parent().unwrap().join(path)
72 };
73
74 canonicalize(&ret)
75}
76
77pub trait RelativePath {
78 fn get_dependency_path(&self, path: &str) -> Result<CanonicalizedPathBuf>;
79}
80
81impl<T: AsRef<Path>> RelativePath for T {
82 fn get_dependency_path(&self, path: &str) -> Result<CanonicalizedPathBuf> {
83 get_dependency_path(self.as_ref(), path)
84 }
85}