-
Notifications
You must be signed in to change notification settings - Fork 25
/
mime_types.rs
124 lines (106 loc) · 3.21 KB
/
mime_types.rs
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::{Error, Result};
use mime::Mime;
use std::{convert::TryFrom, path::Path, str::FromStr};
use url::Url;
// A mime derived from a path or URL
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct MimeType(pub Mime);
impl MimeType {
fn from_ext(ext: &str) -> Result<Mime> {
match &*xdg_mime::SharedMimeInfo::new()
.get_mime_types_from_file_name(ext)
{
[m] if m == &mime::APPLICATION_OCTET_STREAM => {
Err(Error::Ambiguous(ext.into()))
}
[guess, ..] => Ok(guess.clone()),
[] => unreachable!(),
}
}
}
impl From<&Url> for MimeType {
fn from(url: &Url) -> Self {
Self(
format!("x-scheme-handler/{}", url.scheme())
.parse::<Mime>()
.unwrap(),
)
}
}
impl TryFrom<&Path> for MimeType {
type Error = Error;
fn try_from(path: &Path) -> Result<Self> {
let db = xdg_mime::SharedMimeInfo::new();
let guess = db.guess_mime_type().path(&path).guess();
let mime = mime_to_option(guess.mime_type().clone())
.ok_or_else(|| Error::Ambiguous(path.to_owned()))?;
Ok(Self(mime))
}
}
fn mime_to_option(mime: Mime) -> Option<Mime> {
if mime == mime::APPLICATION_OCTET_STREAM {
None
} else {
Some(mime)
}
}
// Mime derived from user input: extension(.pdf) or type like image/jpg
#[derive(Debug)]
pub struct MimeOrExtension(pub Mime);
impl FromStr for MimeOrExtension {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mime = if s.starts_with(".") {
MimeType::from_ext(s)?
} else {
match Mime::from_str(s)? {
m if m.subtype() == "" => return Err(Error::InvalidMime(m)),
proper_mime => proper_mime,
}
};
Ok(Self(mime))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn user_input() -> Result<()> {
assert_eq!(MimeOrExtension::from_str(".pdf")?.0, mime::APPLICATION_PDF);
assert_eq!(
MimeOrExtension::from_str("image/jpeg")?.0,
mime::IMAGE_JPEG
);
"image//jpg".parse::<MimeOrExtension>().unwrap_err();
"image".parse::<MimeOrExtension>().unwrap_err();
Ok(())
}
#[test]
fn from_path() -> Result<()> {
assert_eq!(
MimeType::try_from(Path::new("."))?.0.essence_str(),
"inode/directory"
);
assert_eq!(
MimeType::try_from(Path::new("./tests/rust.vim"))?.0,
"text/plain"
);
assert_eq!(
MimeType::try_from(Path::new("./tests/cat"))?.0,
"application/x-shellscript"
);
assert_eq!(
MimeType::try_from(Path::new("./tests/SettingsWidgetFdoSecrets.ui"))?.0,
"application/x-designer"
);
Ok(())
}
#[test]
fn from_ext() -> Result<()> {
assert_eq!(".mp3".parse::<MimeOrExtension>()?.0, "audio/mpeg");
assert_eq!("audio/mpeg".parse::<MimeOrExtension>()?.0, "audio/mpeg");
".".parse::<MimeOrExtension>().unwrap_err();
"audio/".parse::<MimeOrExtension>().unwrap_err();
Ok(())
}
}