apimock_config/
path_util.rs1use std::{
2 env, io,
3 path::{Path, PathBuf},
4};
5
6#[cfg(test)]
7mod tests;
8
9pub fn current_dir_to_file_parent_dir_relative_path(file_path: &str) -> io::Result<PathBuf> {
19 let parent = Path::new(file_path).parent().ok_or_else(|| {
20 io::Error::new(
21 io::ErrorKind::InvalidInput,
22 format!("failed to get parent dir: {}", file_path),
23 )
24 })?;
25 relative_path(env::current_dir()?.as_path(), parent)
26}
27
28pub fn relative_path(from: &Path, to: &Path) -> io::Result<PathBuf> {
30 let from_abs = std::fs::canonicalize(from)?;
31 let to_abs = std::fs::canonicalize(to)?;
32
33 let mut from_iter = from_abs.components();
34 let mut to_iter = to_abs.components();
35
36 let mut from_rest = vec![];
37 let mut to_rest = vec![];
38 let mut common_prefix = vec![];
40 loop {
41 match (from_iter.next(), to_iter.next()) {
42 (Some(f), Some(t)) if f == t => {
43 common_prefix.push(f);
44 }
45 (Some(f), Some(t)) => {
46 from_rest.push(f);
47 to_rest.push(t);
48 from_rest.extend(from_iter);
49 to_rest.extend(to_iter);
50 break;
51 }
52 (Some(f), None) => {
53 from_rest.push(f);
54 from_rest.extend(from_iter);
55 break;
56 }
57 (None, Some(t)) => {
58 to_rest.push(t);
59 to_rest.extend(to_iter);
60 break;
61 }
62 (None, None) => break,
63 }
64 }
65
66 let mut result = PathBuf::new();
67
68 for _ in from_rest {
69 result.push("..");
70 }
71
72 for t in to_rest {
73 result.push(t.as_os_str());
74 }
75
76 if result.as_os_str().is_empty() {
77 Ok(PathBuf::from("."))
78 } else {
79 Ok(result)
80 }
81}