1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#[macro_use]
extern crate quick_error;
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::PathBuf;
pub fn convert_path(path: &str) -> Result<PathBuf> {
let mut segments = path.split(':');
match segments.next() {
Some(volume_name) => {
let volume_name = volume_name.replace('/', ":");
let mut path = find_volume(&volume_name)?;
for segment in segments {
let segment = segment.replace('/', ":");
path = path.join(segment);
}
Ok(path)
}
None => Err(Error::InvalidHfsPath)
}
}
fn find_volume(name: &str) -> Result<PathBuf> {
for entry in fs::read_dir("/Volumes")? {
let entry = entry?;
if entry.file_name() == OsStr::new(name) {
if entry.file_type()?.is_symlink() {
let link_dest = fs::read_link(entry.path())?;
return Ok(link_dest)
} else {
return Ok(entry.path())
}
}
}
Err(Error::VolumeNotFound(name.into()))
}
pub type Result<T> = ::std::result::Result<T, Error>;
quick_error! {
#[derive(Debug)]
pub enum Error {
InvalidHfsPath {
description("invalid HFS path format")
display("Invalid HFS path format")
}
VolumeNotFound(volume: String) {
description("volume not found")
display("Volume {} not found", volume)
}
Io(err: io::Error) {
description("I/O error")
cause(err)
from()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! expect {
{ $( $hfs:expr => $expected:expr ),+ } => {
[ $( ($hfs, $expected) ),* ]
};
{ } => { [("", ""); 0] };
}
#[test]
fn test_paths() {
let tests = expect! {
"Macintosh SSD:folder1:file" => "/folder1/file",
"Macintosh SSD" => "/",
"Macintosh SSD:folder/with/slashes:file.txt" => "/folder:with:slashes/file.txt",
"BOOTCAMP:Intel:Logs:IntelGFX.log" => "/Volumes/BOOTCAMP/Intel/Logs/IntelGFX.log"
};
for &(hfs, expected) in tests.into_iter() {
let actual = convert_path(hfs).unwrap();
assert_eq!(expected, &actual.display().to_string());
}
}
}