use ref_cast::RefCast;
use crate::http::RawStr;
#[repr(transparent)]
#[derive(RefCast, Debug)]
pub struct FileName(str);
impl FileName {
pub fn new<S: AsRef<str> + ?Sized>(string: &S) -> &FileName {
FileName::ref_cast(string.as_ref())
}
pub fn as_str(&self) -> Option<&str> {
#[cfg(not(unix))]
let (bad_char, bad_name) = {
static BAD_CHARS: &[char] = &[
'.', '<', '>', ':', '"', '/', '\\', '|', '?', '*',
',', ';', '=',
'(', ')', '&', '#',
];
static BAD_NAMES: &[&str] = &[
"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4",
"COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2",
"LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
];
let bad_char = |c| BAD_CHARS.contains(&c) || c.is_control();
let bad_name = |n| BAD_NAMES.contains(&n);
(bad_char, bad_name)
};
#[cfg(unix)]
let (bad_char, bad_name) = {
static BAD_CHARS: &[char] = &[
'.', '/', '\\',
'<', '>', '|', ':', '(', ')', '&', ';', '#', '?', '*',
];
let bad_char = |c| BAD_CHARS.contains(&c) || c.is_control();
let bad_name = |_| false;
(bad_char, bad_name)
};
let file_name = std::path::Path::new(&self.0)
.file_name()
.and_then(|n| n.to_str())
.and_then(|n| n.split(bad_char).find(|s| !s.is_empty()))?;
if file_name.is_empty() || bad_name(file_name) {
return None;
}
Some(file_name)
}
pub fn is_safe(&self) -> bool {
self.as_str().map_or(false, |s| s == &self.0)
}
pub fn dangerous_unsafe_unsanitized_raw(&self) -> &RawStr {
self.0.into()
}
}
impl<'a, S: AsRef<str> + ?Sized> From<&'a S> for &'a FileName {
#[inline]
fn from(string: &'a S) -> Self {
FileName::new(string)
}
}