1use percent_encoding::utf8_percent_encode;
8use crate::utils::HEADER_VALUE_ENCODE_SET;
9
10use std::fmt;
11use std::borrow::Cow;
12
13#[derive(PartialEq, Debug)]
17pub enum DispositionType {
18 Inline,
20 Attachment,
22}
23
24#[derive(Debug)]
25pub enum Filename {
27 Name(Option<String>),
29 Extended(Option<String>, String)
37}
38
39impl Filename {
40 pub fn new() -> Self {
42 Filename::Name(None)
43 }
44
45 pub fn with_name(name: String) -> Self {
47 Filename::Name(Some(name))
48 }
49
50 pub fn with_encoded_name(name: Cow<str>) -> Self {
55 match name.is_ascii() {
56 true => Self::with_name(name.into_owned()),
57 false => match utf8_percent_encode(&name, HEADER_VALUE_ENCODE_SET).into() {
58 std::borrow::Cow::Owned(encoded) => Self::with_extended(None, encoded),
59 std::borrow::Cow::Borrowed(maybe_encoded) => match maybe_encoded == name {
60 true => Self::with_extended(None, maybe_encoded.to_owned()),
61 false => Self::with_name(name.into_owned()),
62 }
63 }
64 }
65 }
66
67 pub fn with_extended(lang: Option<String>, name: String) -> Self {
69 Filename::Extended(lang, name)
70 }
71
72 #[inline]
73 pub fn is_extended(&self) -> bool {
75 match self {
76 Filename::Extended(_, _) => true,
77 _ => false
78 }
79 }
80}
81
82#[derive(Debug)]
83pub enum ContentDisposition {
92 Inline,
94 Attachment(Filename),
96}
97
98impl fmt::Display for ContentDisposition {
99 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100 match self {
101 ContentDisposition::Inline => write!(f, "inline"),
102 ContentDisposition::Attachment(file) => match file {
103 Filename::Name(Some(name)) => write!(f, "attachment; filename=\"{}\"", name),
104 Filename::Name(None) => write!(f, "attachment"),
105 Filename::Extended(lang, value) => {
106 write!(f, "attachment; filename*=utf-8'{}'{}",
107 lang.as_ref().map(|lang| lang.as_str()).unwrap_or(""),
108 value)
109 },
110 },
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::{ContentDisposition, Filename};
118
119 #[test]
120 fn parse_file_name_extended_ascii() {
121 const INPUT: &'static str = "rori.mp4";
122 let file_name = Filename::with_encoded_name(INPUT.into());
123 assert!(!file_name.is_extended());
124 }
125
126 #[test]
127 fn parse_file_name_extended_non_ascii() {
128 const INPUT: &'static str = "ロリへんたい.mp4";
129 let file_name = Filename::with_encoded_name(INPUT.into());
130 assert!(file_name.is_extended());
131 }
132
133 #[test]
134 fn verify_content_disposition_display() {
135 let cd = ContentDisposition::Inline;
136 let cd = format!("{}", cd);
137 assert_eq!(cd, "inline");
138
139 let cd = ContentDisposition::Attachment(Filename::new());
140 let cd = format!("{}", cd);
141 assert_eq!(cd, "attachment");
142
143 let cd = ContentDisposition::Attachment(Filename::with_name("lolka".to_string()));
144 let cd = format!("{}", cd);
145 assert_eq!(cd, "attachment; filename=\"lolka\"");
146
147 let cd = ContentDisposition::Attachment(Filename::with_encoded_name("lolka".into()));
148 let cd = format!("{}", cd);
149 assert_eq!(cd, "attachment; filename=\"lolka\"");
150
151 let cd = ContentDisposition::Attachment(Filename::with_encoded_name("ロリ".into()));
152 let cd = format!("{}", cd);
153 assert_eq!(cd, "attachment; filename*=utf-8\'\'%E3%83%AD%E3%83%AA");
154 }
155}