//! # Mime //! //! Mime is now Media Type, technically, but `Mime` is more immediately //! understandable, so the main type here is `Mime`. //! //! ## What is Mime? //! //! Example mime string: `text/plain` //! //! ``` //! let plain_text: mime::Mime = "text/plain".parse().unwrap(); //! assert_eq!(plain_text, mime::TEXT_PLAIN); //! ``` //! //! ## Inspecting Mimes //! //! ``` //! let mime = mime::TEXT_PLAIN; //! match (mime.type_(), mime.subtype()) { //! (mime::TEXT, mime::PLAIN) => println!("plain text!"), //! (mime::TEXT, _) => println!("structured text"), //! _ => println!("not text"), //! } //! ```
use std::cmp::Ordering; use std::error::Error; use std::fmt; use std::hash::{Hash, Hasher}; use std::str::FromStr; use std::slice;
mod parse;
/// A parsed mime or media type. #[derive(Clone)] pubstruct Mime {
source: Source,
slash: usize,
plus: Option<usize>,
params: ParamSource,
}
/// A section of a `Mime`. /// /// For instance, for the Mime `image/svg+xml`, it contains 3 `Name`s, /// `image`, `svg`, and `xml`. /// /// In most cases, `Name`s are compared ignoring case. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pubstruct Name<'a> { // TODO: optimize with an Atom-like thing // There a `const` Names, and so it is possible for the statis strings // to havea different memory address. Additionally, when used in match // statements, the strings are compared with a memcmp, possibly even // if the address and length are the same. // // Being an enum with an Atom variant that is a usize (and without a // string pointer and boolean) would allow for faster comparisons.
source: &'a str,
insensitive: bool,
}
/// An error when parsing a `Mime` from a string. #[derive(Debug)] pubstruct FromStrError {
inner: parse::ParseError,
}
impl Error for FromStrError { // Minimum Rust is 1.15, Error::description was still required then #[allow(deprecated)] fn description(&self) -> &str { self.s()
}
}
impl Mime { /// Get the top level media type for this `Mime`. /// /// # Example /// /// ``` /// let mime = mime::TEXT_PLAIN; /// assert_eq!(mime.type_(), "text"); /// assert_eq!(mime.type_(), mime::TEXT); /// ``` #[inline] pubfn type_(&self) -> Name {
Name {
source: &self.source.as_ref()[..self.slash],
insensitive: true,
}
}
/// Get the subtype of this `Mime`. /// /// # Example /// /// ``` /// let mime = mime::TEXT_PLAIN; /// assert_eq!(mime.subtype(), "plain"); /// assert_eq!(mime.subtype(), mime::PLAIN); /// ``` #[inline] pubfn subtype(&self) -> Name { let end = self.plus.unwrap_or_else(|| { returnself.semicolon().unwrap_or(self.source.as_ref().len())
});
Name {
source: &self.source.as_ref()[self.slash + 1..end],
insensitive: true,
}
}
/// Get an optional +suffix for this `Mime`. /// /// # Example /// /// ``` /// let svg = "image/svg+xml".parse::<mime::Mime>().unwrap(); /// assert_eq!(svg.suffix(), Some(mime::XML)); /// assert_eq!(svg.suffix().unwrap(), "xml"); /// /// /// assert!(mime::TEXT_PLAIN.suffix().is_none()); /// ``` #[inline] pubfn suffix(&self) -> Option<Name> { let end = self.semicolon().unwrap_or(self.source.as_ref().len()); self.plus.map(|idx| Name {
source: &self.source.as_ref()[idx + 1..end],
insensitive: true,
})
}
/// Look up a parameter by name. /// /// # Example /// /// ``` /// let mime = mime::TEXT_PLAIN_UTF_8; /// assert_eq!(mime.get_param(mime::CHARSET), Some(mime::UTF_8)); /// assert_eq!(mime.get_param("charset").unwrap(), "utf-8"); /// assert!(mime.get_param("boundary").is_none()); /// /// let mime = "multipart/form-data; boundary=ABCDEFG".parse::<mime::Mime>().unwrap(); /// assert_eq!(mime.get_param(mime::BOUNDARY).unwrap(), "ABCDEFG"); /// ``` pubfn get_param<'a, N>(&'a self, attr: N) -> Option<Name<'a>> where N: PartialEq<Name<'a>> { self.params().find(|e| attr == e.0).map(|e| e.1)
}
/// Returns an iterator over the parameters. #[inline] pubfn params<'a>(&'a self) -> Params<'a> { let inner = matchself.params {
ParamSource::Utf8(_) => ParamsInner::Utf8,
ParamSource::Custom(_, ref params) => {
ParamsInner::Custom {
source: &self.source,
params: params.iter(),
}
}
ParamSource::None => ParamsInner::None,
};
Params(inner)
}
/// Return a `&str` of the Mime's ["essence"][essence]. /// /// [essence]: https://mimesniff.spec.whatwg.org/#mime-type-essence pubfn essence_str(&self) -> &str { let end = self.semicolon().unwrap_or(self.source.as_ref().len());
fn eq_ascii(a: &str, b: &str) -> bool { // str::eq_ignore_ascii_case didn't stabilize until Rust 1.23. // So while our MSRV is 1.15, gotta import this trait. #[allow(deprecated, unused)] use std::ascii::AsciiExt;
if sensitive { if !eq_ascii(&a[..a_end], &b[..b_end]) { returnfalse;
}
} else { if &a[..a_end] != &b[..b_end] { returnfalse;
}
}
a = &a[a_end..];
b = &b[b_end..];
}
}
}
impl PartialEq for Mime { #[inline] fn eq(&self, other: &Mime) -> bool { match (self.atom(), other.atom()) { // TODO: // This could optimize for when there are no customs parameters. // Any parsed mime has already been lowercased, so if there aren't // any parameters that are case sensistive, this can skip the // eq_ascii, and just use a memcmp instead.
(0, _) |
(_, 0) => mime_eq_str(self, other.source.as_ref()),
(a, b) => a == b,
}
}
}
impl<'a> Name<'a> { /// Get the value of this `Name` as a string. /// /// Note that the borrow is not tied to `&self` but the `'a` lifetime, allowing the /// string to outlive `Name`. Alternately, there is an `impl<'a> From<Name<'a>> for &'a str` /// which isn't rendered by Rustdoc, that can be accessed using `str::from(name)` or `name.into()`. pubfn as_str(&self) -> &'a str { self.source
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.