use percent_encoding::utf8_percent_encode;
use crate::utils::HEADER_VALUE_ENCODE_SET;
use std::fmt;
use std::borrow::Cow;
#[derive(PartialEq, Debug)]
pub enum DispositionType {
Inline,
Attachment,
}
#[derive(Debug)]
pub enum Filename {
Name(Option<String>),
Extended(Option<String>, String)
}
impl Filename {
pub fn new() -> Self {
Filename::Name(None)
}
pub fn with_name(name: String) -> Self {
Filename::Name(Some(name))
}
pub fn with_encoded_name(name: Cow<str>) -> Self {
match name.is_ascii() {
true => Self::with_name(name.into_owned()),
false => match utf8_percent_encode(&name, HEADER_VALUE_ENCODE_SET).into() {
std::borrow::Cow::Owned(encoded) => Self::with_extended(None, encoded),
std::borrow::Cow::Borrowed(maybe_encoded) => match maybe_encoded == name {
true => Self::with_extended(None, maybe_encoded.to_owned()),
false => Self::with_name(name.into_owned()),
}
}
}
}
pub fn with_extended(lang: Option<String>, name: String) -> Self {
Filename::Extended(lang, name)
}
#[inline]
pub fn is_extended(&self) -> bool {
match self {
Filename::Extended(_, _) => true,
_ => false
}
}
}
#[derive(Debug)]
pub enum ContentDisposition {
Inline,
Attachment(Filename),
}
impl fmt::Display for ContentDisposition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ContentDisposition::Inline => write!(f, "inline"),
ContentDisposition::Attachment(file) => match file {
Filename::Name(Some(name)) => write!(f, "attachment; filename=\"{}\"", name),
Filename::Name(None) => write!(f, "attachment"),
Filename::Extended(lang, value) => {
write!(f, "attachment; filename*=utf-8'{}'{}",
lang.as_ref().map(|lang| lang.as_str()).unwrap_or(""),
value)
},
},
}
}
}
#[cfg(test)]
mod tests {
use super::{ContentDisposition, Filename};
#[test]
fn parse_file_name_extended_ascii() {
const INPUT: &'static str = "rori.mp4";
let file_name = Filename::with_encoded_name(INPUT.into());
assert!(!file_name.is_extended());
}
#[test]
fn parse_file_name_extended_non_ascii() {
const INPUT: &'static str = "ロリへんたい.mp4";
let file_name = Filename::with_encoded_name(INPUT.into());
assert!(file_name.is_extended());
}
#[test]
fn verify_content_disposition_display() {
let cd = ContentDisposition::Inline;
let cd = format!("{}", cd);
assert_eq!(cd, "inline");
let cd = ContentDisposition::Attachment(Filename::new());
let cd = format!("{}", cd);
assert_eq!(cd, "attachment");
let cd = ContentDisposition::Attachment(Filename::with_name("lolka".to_string()));
let cd = format!("{}", cd);
assert_eq!(cd, "attachment; filename=\"lolka\"");
let cd = ContentDisposition::Attachment(Filename::with_encoded_name("lolka".into()));
let cd = format!("{}", cd);
assert_eq!(cd, "attachment; filename=\"lolka\"");
let cd = ContentDisposition::Attachment(Filename::with_encoded_name("ロリ".into()));
let cd = format!("{}", cd);
assert_eq!(cd, "attachment; filename*=utf-8\'\'%E3%83%AD%E3%83%AA");
}
}