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
use super::help::bug_report;
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use roa_core::http::header::HeaderValue;
use roa_core::Result;
use std::fmt::{self, Display, Formatter};
const HTTP_VALUE: &AsciiSet = &CONTROLS
.add(b' ')
.add(b'"')
.add(b'%')
.add(b'\'')
.add(b'(')
.add(b')')
.add(b'*')
.add(b',')
.add(b'/')
.add(b':')
.add(b';')
.add(b'<')
.add(b'-')
.add(b'>')
.add(b'?')
.add(b'[')
.add(b'\\')
.add(b']')
.add(b'{')
.add(b'}');
#[derive(Clone, Debug, PartialEq)]
pub enum DispositionType {
Inline,
Attachment,
}
pub struct ContentDisposition {
typ: DispositionType,
encoded_filename: Option<String>,
}
impl ContentDisposition {
#[inline]
pub(crate) fn new(typ: DispositionType, filename: Option<&str>) -> Self {
Self {
typ,
encoded_filename: filename
.map(|name| utf8_percent_encode(name, HTTP_VALUE).to_string()),
}
}
#[inline]
pub fn value(&self) -> Result<HeaderValue> {
let value_str = self.to_string();
value_str.parse().map_err(|err| {
bug_report(format!(
"{}\n{} is not a valid header value",
err, value_str
))
})
}
}
impl Display for ContentDisposition {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match &self.encoded_filename {
None => f.write_fmt(format_args!("{}", self.typ)),
Some(name) => f.write_fmt(format_args!(
"{}; filename={}; filename*=UTF-8''{}",
self.typ, name, name
)),
}
}
}
impl Display for DispositionType {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
DispositionType::Inline => f.write_str("inline"),
DispositionType::Attachment => f.write_str("attachment"),
}
}
}