ferrin_spec/shared/
media_type.rs1use serde::Deserialize;
4use serde::Serialize;
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
12#[serde(transparent)]
13pub struct MediaType(String);
14
15impl MediaType {
16 #[must_use]
18 pub fn new(value: impl Into<String>) -> Self {
19 Self(value.into())
20 }
21
22 #[must_use]
24 pub fn as_str(&self) -> &str {
25 &self.0
26 }
27
28 #[must_use]
30 pub fn into_string(self) -> String {
31 self.0
32 }
33
34 #[must_use]
38 pub fn is_full(&self) -> bool {
39 match self.0.split_once('/') {
40 Some((top, sub)) => !top.is_empty() && !sub.is_empty() && sub != "*",
41 None => false,
42 }
43 }
44
45 #[must_use]
47 pub fn top_level(&self) -> String {
48 let top = self
49 .0
50 .split_once('/')
51 .map_or(self.0.as_str(), |(top, _)| top);
52 top.trim().to_ascii_lowercase()
53 }
54
55 #[must_use]
57 pub fn subtype(&self) -> Option<&str> {
58 let (_, sub) = self.0.split_once('/')?;
59 let sub = sub.split(';').next().unwrap_or(sub).trim();
60 (!sub.is_empty()).then_some(sub)
61 }
62
63 #[must_use]
68 pub fn normalize(&self) -> MediaType {
69 let without_params = self.0.split(';').next().unwrap_or(&self.0).trim();
70 let lower = without_params.to_ascii_lowercase();
71 match lower.split_once('/') {
72 Some((top, "*")) => MediaType(top.to_owned()),
73 Some((top, "")) => MediaType(top.to_owned()),
74 _ => MediaType(lower),
75 }
76 }
77
78 #[must_use]
83 pub fn matches(&self, pattern: &MediaType) -> bool {
84 let this = self.normalize();
85 let pattern = pattern.normalize();
86 if this == pattern {
87 return true;
88 }
89 if pattern.0.contains('/') {
90 return false;
91 }
92 this.top_level() == pattern.0
93 }
94}
95
96impl std::fmt::Display for MediaType {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 f.write_str(&self.0)
99 }
100}
101
102impl From<String> for MediaType {
103 fn from(value: String) -> Self {
104 Self(value)
105 }
106}
107
108impl From<&str> for MediaType {
109 fn from(value: &str) -> Self {
110 Self(value.to_owned())
111 }
112}
113
114impl From<MediaType> for String {
115 fn from(value: MediaType) -> Self {
116 value.0
117 }
118}
119
120impl AsRef<str> for MediaType {
121 fn as_ref(&self) -> &str {
122 &self.0
123 }
124}
125
126impl PartialEq<str> for MediaType {
127 fn eq(&self, other: &str) -> bool {
128 self.0 == other
129 }
130}
131
132impl PartialEq<&str> for MediaType {
133 fn eq(&self, other: &&str) -> bool {
134 self.0 == *other
135 }
136}