mail_template/
path_rebase.rs1use std::{
2 path::{Path, PathBuf},
3 mem
4};
5
6use mail_core::{IRI, Resource};
7use failure::{Fail, Context};
8
9#[derive(Fail, Debug)]
10#[fail(display = "unsupported path, only paths with following constraint are allowed: {}", _0)]
11pub struct UnsupportedPathError(Context<&'static str>);
12
13impl UnsupportedPathError {
14 pub fn new(violated_constraint: &'static str) -> Self {
15 UnsupportedPathError(Context::new(violated_constraint))
16 }
17}
18
19pub trait PathRebaseable {
20 fn rebase_to_include_base_dir(&mut self, base_dir: impl AsRef<Path>)
29 -> Result<(), UnsupportedPathError>;
30
31 fn rebase_to_exclude_base_dir(&mut self, base_dir: impl AsRef<Path>)
40 -> Result<(), UnsupportedPathError>;
41}
42
43impl PathRebaseable for PathBuf {
44 fn rebase_to_include_base_dir(&mut self, base_dir: impl AsRef<Path>)
45 -> Result<(), UnsupportedPathError>
46 {
47 let new_path;
48 if self.is_relative() {
49 new_path = base_dir.as_ref().join(&self);
50 } else {
51 return Ok(());
52 }
53 mem::replace(self, new_path);
54 Ok(())
55 }
56
57 fn rebase_to_exclude_base_dir(&mut self, base_dir: impl AsRef<Path>)
58 -> Result<(), UnsupportedPathError>
59 {
60 let new_path;
61 if let Ok(path) = self.strip_prefix(base_dir) {
62 new_path = path.to_owned();
63 } else {
64 return Ok(());
65 }
66 mem::replace(self, new_path);
67 Ok(())
68 }
69}
70
71impl PathRebaseable for IRI {
72 fn rebase_to_include_base_dir(&mut self, base_dir: impl AsRef<Path>)
73 -> Result<(), UnsupportedPathError>
74 {
75 if self.scheme() != "path" {
76 return Ok(());
77 }
78
79 let new_tail = {
80 let path = Path::new(self.tail());
81 if path.is_relative() {
82 base_dir.as_ref().join(path)
83 } else {
84 return Ok(());
85 }
86 };
87
88 let new_tail = new_tail.to_str()
89 .ok_or_else(|| UnsupportedPathError::new("utf-8"))?;
90
91 let new_iri = self.with_tail(new_tail);
92 mem::replace(self, new_iri);
93 Ok(())
94 }
95
96 fn rebase_to_exclude_base_dir(&mut self, base_dir: impl AsRef<Path>)
97 -> Result<(), UnsupportedPathError>
98 {
99 if self.scheme() != "path" {
100 return Ok(());
101 }
102
103 let new_iri = {
104 let path = Path::new(self.tail());
105
106 if let Ok(path) = path.strip_prefix(base_dir) {
107 let new_tail = path.to_str().unwrap();
110 self.with_tail(new_tail)
111 } else {
112 return Ok(());
113 }
114 };
115
116 mem::replace(self, new_iri);
117 Ok(())
118 }
119}
120
121impl PathRebaseable for Resource {
122 fn rebase_to_include_base_dir(&mut self, base_dir: impl AsRef<Path>)
123 -> Result<(), UnsupportedPathError>
124 {
125 if let &mut Resource::Source(ref mut source) = self {
126 source.iri.rebase_to_include_base_dir(base_dir)?;
127 }
128 Ok(())
129 }
130
131 fn rebase_to_exclude_base_dir(&mut self, base_dir: impl AsRef<Path>)
132 -> Result<(), UnsupportedPathError>
133 {
134 if let &mut Resource::Source(ref mut source) = self {
135 source.iri.rebase_to_exclude_base_dir(base_dir)?;
136 }
137 Ok(())
138 }
139
140}
141
142
143
144#[cfg(test)]
145mod test {
146 use super::*;
147 use mail_core::Source;
148
149 #[test]
150 fn rebase_on_path() {
151 let mut path = Path::new("/prefix/suffix.yup").to_owned();
152 path.rebase_to_exclude_base_dir("/prefix").unwrap();
153 assert_eq!(path, Path::new("suffix.yup"));
154 path.rebase_to_include_base_dir("./nfix").unwrap();
155 path.rebase_to_include_base_dir("/mfix").unwrap();
156 assert_eq!(path, Path::new("/mfix/nfix/suffix.yup"));
157 path.rebase_to_exclude_base_dir("/wrong").unwrap();
158 assert_eq!(path, Path::new("/mfix/nfix/suffix.yup"));
159 }
160
161 #[test]
162 fn rebase_on_iri() {
163 let mut iri: IRI = "path:/prefix/suffix.yup".parse().unwrap();
164 iri.rebase_to_exclude_base_dir("/prefix").unwrap();
165 assert_eq!(iri.as_str(), "path:suffix.yup");
166 iri.rebase_to_include_base_dir("nfix").unwrap();
167 iri.rebase_to_include_base_dir("/mfix").unwrap();
168 assert_eq!(iri.as_str(), "path:/mfix/nfix/suffix.yup");
169 iri.rebase_to_exclude_base_dir("/wrong").unwrap();
170 assert_eq!(iri.as_str(), "path:/mfix/nfix/suffix.yup");
171 }
172
173 #[test]
174 fn rebase_on_resource() {
175 let mut resource = Resource::Source(Source {
176 iri: "path:abc/def".parse().unwrap(),
177 use_media_type: Default::default(),
178 use_file_name: Default::default()
179 });
180
181 resource.rebase_to_include_base_dir("./abc").unwrap();
182 resource.rebase_to_include_base_dir("/pre").unwrap();
183 resource.rebase_to_exclude_base_dir("/pre").unwrap();
184 resource.rebase_to_exclude_base_dir("abc").unwrap();
185 resource.rebase_to_include_base_dir("abc").unwrap();
186
187 if let Resource::Source(Source { iri, ..}) = resource {
188 assert_eq!(iri.as_str(), "path:abc/abc/def");
189 } else { unreachable!() }
190 }
191}