1use core::cmp;
2use core::fmt;
3use core::hash;
4use core::ptr;
5use core::slice;
6use core::str;
7
8use crate::bindings::{ngx_pool_t, ngx_str_t};
9use crate::detail;
10
11impl ngx_str_t {
12 #[inline]
17 pub fn as_bytes(&self) -> &[u8] {
18 if self.is_empty() {
19 &[]
20 } else {
21 unsafe { slice::from_raw_parts(self.data, self.len) }
23 }
24 }
25
26 #[inline]
28 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
29 if self.is_empty() {
30 &mut []
31 } else {
32 unsafe { slice::from_raw_parts_mut(self.data, self.len) }
34 }
35 }
36
37 #[inline]
39 pub fn is_empty(&self) -> bool {
40 self.len == 0
41 }
42
43 pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
50 str::from_utf8(self.as_bytes())
51 }
52
53 pub const fn empty() -> Self {
57 ngx_str_t {
58 len: 0,
59 data: ptr::null_mut(),
60 }
61 }
62
63 pub unsafe fn from_bytes(pool: *mut ngx_pool_t, src: &[u8]) -> Option<Self> {
69 detail::bytes_to_uchar(pool, src).map(|data| Self {
70 data,
71 len: src.len(),
72 })
73 }
74
75 pub unsafe fn from_str(pool: *mut ngx_pool_t, data: &str) -> Self {
90 ngx_str_t {
91 data: detail::str_to_uchar(pool, data),
92 len: data.len(),
93 }
94 }
95
96 pub fn split_at(&self, mid: usize) -> Option<(ngx_str_t, ngx_str_t)> {
102 if mid > self.len {
103 return None;
104 }
105
106 Some((
107 ngx_str_t {
108 data: self.data,
109 len: mid,
110 },
111 ngx_str_t {
112 data: unsafe { self.data.add(mid) },
113 len: self.len - mid,
114 },
115 ))
116 }
117
118 pub fn strip_prefix(&self, prefix: impl AsRef<[u8]>) -> Option<ngx_str_t> {
129 let prefix = prefix.as_ref();
130 if self.as_bytes().starts_with(prefix) {
131 self.split_at(prefix.len()).map(|x| x.1)
132 } else {
133 None
134 }
135 }
136
137 pub fn strip_suffix(&self, suffix: impl AsRef<[u8]>) -> Option<ngx_str_t> {
148 let suffix = suffix.as_ref();
149 if self.as_bytes().ends_with(suffix) {
150 self.split_at(self.len - suffix.len()).map(|x| x.0)
151 } else {
152 None
153 }
154 }
155}
156
157impl AsRef<[u8]> for ngx_str_t {
158 fn as_ref(&self) -> &[u8] {
159 self.as_bytes()
160 }
161}
162
163impl AsMut<[u8]> for ngx_str_t {
164 fn as_mut(&mut self) -> &mut [u8] {
165 self.as_bytes_mut()
166 }
167}
168
169impl Default for ngx_str_t {
170 fn default() -> Self {
171 Self::empty()
172 }
173}
174
175impl fmt::Display for ngx_str_t {
176 #[inline]
177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178 detail::display_bytes(f, self.as_bytes())
179 }
180}
181
182impl From<ngx_str_t> for &[u8] {
183 fn from(s: ngx_str_t) -> Self {
184 if s.len == 0 || s.data.is_null() {
185 return Default::default();
186 }
187 unsafe { slice::from_raw_parts(s.data, s.len) }
188 }
189}
190
191impl hash::Hash for ngx_str_t {
192 fn hash<H: hash::Hasher>(&self, state: &mut H) {
193 self.as_bytes().hash(state)
194 }
195}
196
197impl PartialEq for ngx_str_t {
198 fn eq(&self, other: &Self) -> bool {
199 PartialEq::eq(self.as_bytes(), other.as_bytes())
200 }
201}
202
203impl Eq for ngx_str_t {}
204
205impl PartialOrd<Self> for ngx_str_t {
206 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
207 Some(self.cmp(other))
208 }
209}
210
211impl Ord for ngx_str_t {
212 fn cmp(&self, other: &Self) -> cmp::Ordering {
213 Ord::cmp(self.as_bytes(), other.as_bytes())
214 }
215}
216
217impl TryFrom<ngx_str_t> for &str {
218 type Error = str::Utf8Error;
219
220 fn try_from(s: ngx_str_t) -> Result<Self, Self::Error> {
221 str::from_utf8(s.into())
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn ngx_str_prefix() {
231 let s = "key=value";
232 let s = ngx_str_t {
233 data: s.as_ptr().cast_mut(),
234 len: s.len(),
235 };
236
237 assert_eq!(
238 s.strip_prefix("key=").as_ref().map(ngx_str_t::as_bytes),
239 Some("value".as_bytes())
240 );
241
242 assert_eq!(s.strip_prefix("test"), None);
243
244 assert_eq!(
245 s.strip_suffix("value").as_ref().map(ngx_str_t::as_bytes),
246 Some("key=".as_bytes())
247 );
248
249 assert_eq!(s.strip_suffix("test"), None);
250 }
251}