lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

media_type.rs (2368B)


      1 //! Canonical Blossom media-type values.
      2 //!
      3 //! [`MediaType`] rejects wildcards and duplicate parameters, then emits a
      4 //! deterministic parameter order. Parsing describes declared media type only;
      5 //! callers remain responsible for sniffing or applying content policy where
      6 //! untrusted bytes require it.
      7 
      8 #[cfg(feature = "serde")]
      9 use alloc::string::String;
     10 use alloc::{collections::BTreeMap, vec::Vec};
     11 use core::{fmt, str::FromStr};
     12 use mediatype::{MediaTypeBuf, ReadParams};
     13 
     14 use crate::Error;
     15 
     16 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
     17 pub struct MediaType(MediaTypeBuf);
     18 
     19 impl MediaType {
     20     pub fn parse(value: &str) -> Result<Self, Error> {
     21         let parsed = MediaTypeBuf::from_str(value).map_err(|_| Error::InvalidMediaType)?;
     22         if parsed.as_str() != value || parsed.ty().as_str() == "*" || parsed.subty().as_str() == "*"
     23         {
     24             return Err(Error::InvalidMediaType);
     25         }
     26         let canonical = parsed.canonicalize();
     27         let mut unique_params = BTreeMap::new();
     28         for (name, value) in canonical.params() {
     29             if unique_params.insert(name, value).is_some() {
     30                 return Err(Error::InvalidMediaType);
     31             }
     32         }
     33         let ordered_params = unique_params.into_iter().collect::<Vec<_>>();
     34         Ok(Self(MediaTypeBuf::from_parts(
     35             canonical.ty(),
     36             canonical.subty(),
     37             canonical.suffix(),
     38             &ordered_params,
     39         )))
     40     }
     41 
     42     pub fn as_str(&self) -> &str {
     43         self.0.as_str()
     44     }
     45 }
     46 
     47 impl fmt::Display for MediaType {
     48     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     49         f.write_str(self.as_str())
     50     }
     51 }
     52 
     53 impl FromStr for MediaType {
     54     type Err = Error;
     55 
     56     fn from_str(value: &str) -> Result<Self, Self::Err> {
     57         Self::parse(value)
     58     }
     59 }
     60 
     61 #[cfg(feature = "serde")]
     62 impl serde::Serialize for MediaType {
     63     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     64     where
     65         S: serde::Serializer,
     66     {
     67         serializer.serialize_str(self.as_str())
     68     }
     69 }
     70 
     71 #[cfg(feature = "serde")]
     72 impl<'de> serde::Deserialize<'de> for MediaType {
     73     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     74     where
     75         D: serde::Deserializer<'de>,
     76     {
     77         let value = String::deserialize(deserializer)?;
     78         Self::parse(&value).map_err(serde::de::Error::custom)
     79     }
     80 }