1use std::path::PathBuf;
2
3use derive_more::{AsRef, Deref};
4use resolve_path::PathResolveExt;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6
7mod std_impl;
8
9#[derive(Deref, AsRef)]
19pub struct ResolvedPathBuf {
20 #[as_ref]
21 #[deref]
22 resolved: PathBuf,
23 original: Option<PathBuf>,
27}
28
29impl TryFrom<PathBuf> for ResolvedPathBuf {
30 type Error = std::io::Error;
31
32 fn try_from(original: PathBuf) -> Result<Self, Self::Error> {
33 let resolved = original.try_resolve()?.to_path_buf();
34 let original = (resolved != original).then_some(original);
35 Ok(Self { resolved, original })
36 }
37}
38
39impl TryFrom<&str> for ResolvedPathBuf {
40 type Error = std::io::Error;
41
42 fn try_from(path: &str) -> Result<Self, Self::Error> {
43 let original: PathBuf = path.into();
44 let resolved = original.try_resolve()?.to_path_buf();
45 let original = (resolved != original).then_some(original);
46 Ok(Self { resolved, original })
47 }
48}
49
50impl Serialize for ResolvedPathBuf {
51 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
52 where
53 S: Serializer,
54 {
55 self.original
56 .as_ref()
57 .unwrap_or(&self.resolved)
58 .serialize(serializer)
59 }
60}
61
62impl<'de> Deserialize<'de> for ResolvedPathBuf {
63 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
64 where
65 D: Deserializer<'de>,
66 {
67 let original: PathBuf = PathBuf::deserialize(deserializer)?;
68 ResolvedPathBuf::try_from(original)
69 .map_err(|e| serde::de::Error::custom(format!("Failed to resolve path: {e}")))
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn test_as_ref() {
79 let p = ResolvedPathBuf::try_from(PathBuf::from(r"~/x")).unwrap();
80 let x: &PathBuf = p.as_ref();
81 assert_eq!(&p.resolved, x);
82 assert!(x.is_absolute());
83 }
84
85 #[test]
86 fn test_deref() {
87 let p = ResolvedPathBuf::try_from(PathBuf::from(r"~/x")).unwrap();
88 let x: &PathBuf = &p;
89 assert_eq!(&p.resolved, x);
90 assert!(x.is_absolute());
91 }
92
93 #[test]
94 fn serde_serialize() {
95 let o = PathBuf::from(r"~/x");
96 let r = ResolvedPathBuf::try_from(o.clone()).unwrap();
97
98 let o_json = serde_json::to_vec(&o).unwrap();
99 let r_json = serde_json::to_vec(&r).unwrap();
100
101 assert_eq!(o_json, r_json);
102 }
103
104 #[test]
105 fn serde_deserialize() {
106 let o = PathBuf::from(r"~/x");
107 let o_json = serde_json::to_vec(&o).unwrap();
108 let r: ResolvedPathBuf = serde_json::from_slice(&o_json).unwrap();
109 assert_eq!(r.original, Some(o));
110 assert!(r.is_absolute());
111 }
112}