Skip to main content

sark_http/field/storage/
name.rs

1use std::borrow::Cow;
2
3use o3::alloc::{self, StrExt as _};
4
5use crate::{
6    field::{self, Error, head::known::Known, storage},
7    h1::field::Entry,
8};
9
10/// An owned or borrowed HTTP field name validated before storage.
11#[repr(transparent)]
12#[derive(Copy, Debug, PartialEq, Eq)]
13pub struct Name<T>(pub(in crate::field) T);
14
15impl<T: Copy> Clone for Name<T> {
16    fn clone(&self) -> Self {
17        *self
18    }
19}
20
21impl<T: storage::Bytes> Name<T> {
22    pub fn new(name: T) -> Result<Self, Error> {
23        Error::name(name.as_bytes())?;
24        Ok(Self(name))
25    }
26
27    pub fn as_bytes(&self) -> &[u8] {
28        self.0.as_bytes()
29    }
30
31    pub fn as_str(&self) -> &str {
32        field::raw::name(self.borrow())
33    }
34
35    pub fn borrow(&self) -> Name<&[u8]> {
36        Name(self.as_bytes())
37    }
38}
39
40impl<T> Name<T> {
41    pub fn into_inner(self) -> T {
42        self.0
43    }
44}
45
46impl<'a> Name<&'a [u8]> {
47    pub const fn try_bytes(name: &'a [u8]) -> Result<Self, Error> {
48        match Error::name(name) {
49            Ok(()) => Ok(Self(name)),
50            Err(cause) => Err(cause),
51        }
52    }
53
54    pub const fn get(self) -> &'a [u8] {
55        self.0
56    }
57}
58
59impl<'field> Name<Cow<'field, str>> {
60    pub const CONTENT_TYPE: Self = Self(Cow::Borrowed(Known::ContentType.as_str()));
61
62    pub fn try_copy(name: &[u8]) -> Result<Self, storage::Error> {
63        if let Some(known) = Known::from_bytes(name) {
64            return Ok(Self(Cow::from(known)));
65        }
66        let name = Name::try_bytes(name).map_err(storage::Error::Syntax)?;
67        Self::try_lowered(name).map_err(storage::Error::Allocation)
68    }
69
70    pub(crate) fn try_entry(field: Entry<'_>) -> Result<Self, alloc::Exhausted> {
71        let name = field.name();
72        match Known::from_bytes(name) {
73            Some(known) => Ok(Self(Cow::from(known))),
74            None => Self::try_lowered(Name(name)),
75        }
76    }
77
78    fn try_lowered(name: Name<&[u8]>) -> Result<Self, alloc::Exhausted> {
79        let mut name = name.as_str().try_to_owned()?;
80        name.make_ascii_lowercase();
81        Ok(Self(Cow::Owned(name)))
82    }
83
84    pub fn try_owned(self) -> Result<Name<String>, alloc::Exhausted> {
85        Ok(Name(storage::try_own(self.0)?))
86    }
87
88    pub(crate) fn try_lower(mut self) -> Result<Self, alloc::Exhausted> {
89        if self.0.bytes().any(|byte| byte.is_ascii_uppercase()) {
90            match Known::from_bytes(self.0.as_bytes()) {
91                Some(known) => self.0 = Cow::from(known),
92                None => {
93                    let mut name = storage::try_own(self.0)?;
94                    name.make_ascii_lowercase();
95                    self.0 = Cow::Owned(name);
96                }
97            }
98        }
99        Ok(self)
100    }
101}