ngx/core/
string.rs

1use core::slice;
2use core::str::{self, Utf8Error};
3
4#[cfg(all(not(feature = "std"), feature = "alloc"))]
5use alloc::{borrow::Cow, string::String};
6#[cfg(feature = "std")]
7use std::{borrow::Cow, string::String};
8
9use crate::ffi::{ngx_str_t, u_char};
10
11/// Static string initializer for [`ngx_str_t`].
12///
13/// The resulting byte string is always nul-terminated (just like a C string).
14///
15/// [`ngx_str_t`]: https://nginx.org/en/docs/dev/development_guide.html#string_overview
16#[macro_export]
17macro_rules! ngx_string {
18    ($s:expr) => {{
19        $crate::ffi::ngx_str_t {
20            len: $s.len() as _,
21            data: concat!($s, "\0").as_ptr() as *mut u8,
22        }
23    }};
24}
25
26/// Representation of a borrowed [Nginx string].
27///
28/// [Nginx string]: https://nginx.org/en/docs/dev/development_guide.html#string_overview
29pub struct NgxStr([u_char]);
30
31impl NgxStr {
32    /// Create an [`NgxStr`] from an [`ngx_str_t`].
33    ///
34    /// [`ngx_str_t`]: https://nginx.org/en/docs/dev/development_guide.html#string_overview
35    ///
36    /// # Safety
37    ///
38    /// The caller has provided a valid `ngx_str_t` with a `data` pointer that points
39    /// to range of bytes of at least `len` bytes, whose content remains valid and doesn't
40    /// change for the lifetime of the returned `NgxStr`.
41    pub unsafe fn from_ngx_str<'a>(str: ngx_str_t) -> &'a NgxStr {
42        slice::from_raw_parts(str.data, str.len).into()
43    }
44
45    /// Access the [`NgxStr`] as a byte slice.
46    pub fn as_bytes(&self) -> &[u8] {
47        &self.0
48    }
49
50    /// Yields a `&str` slice if the [`NgxStr`] contains valid UTF-8.
51    pub fn to_str(&self) -> Result<&str, Utf8Error> {
52        str::from_utf8(self.as_bytes())
53    }
54
55    /// Converts an [`NgxStr`] into a [`Cow<str>`], replacing invalid UTF-8 sequences.
56    ///
57    /// See [`String::from_utf8_lossy`].
58    #[cfg(feature = "alloc")]
59    pub fn to_string_lossy(&self) -> Cow<str> {
60        String::from_utf8_lossy(self.as_bytes())
61    }
62
63    /// Returns `true` if the [`NgxStr`] is empty, otherwise `false`.
64    pub fn is_empty(&self) -> bool {
65        self.0.is_empty()
66    }
67}
68
69impl From<&[u8]> for &NgxStr {
70    fn from(bytes: &[u8]) -> Self {
71        // SAFETY: An `NgxStr` is identical to a `[u8]` slice, given `u_char` is an alias for `u8`.
72        unsafe { &*(bytes as *const [u8] as *const NgxStr) }
73    }
74}
75
76impl From<&str> for &NgxStr {
77    fn from(s: &str) -> Self {
78        s.as_bytes().into()
79    }
80}
81
82impl AsRef<[u8]> for NgxStr {
83    fn as_ref(&self) -> &[u8] {
84        self.as_bytes()
85    }
86}
87
88impl Default for &NgxStr {
89    fn default() -> Self {
90        // SAFETY: The empty `ngx_str_t` is always a valid Nginx string.
91        unsafe { NgxStr::from_ngx_str(ngx_str_t::default()) }
92    }
93}