Skip to main content

sark_http/field/storage/
lower.rs

1use std::borrow::Cow;
2
3use o3::alloc::{self, BoxSliceExt as _};
4
5use crate::{
6    field::{Error, Name, storage},
7    h1::field,
8};
9
10/// An immutable lowercase HTTP field name admitted for outgoing protocols.
11#[repr(transparent)]
12#[derive(Copy, Debug, PartialEq, Eq)]
13pub struct Lower<T>(pub(in crate::field) T);
14
15impl<T: Copy> Clone for Lower<T> {
16    fn clone(&self) -> Self {
17        *self
18    }
19}
20
21impl<T: storage::Bytes> Lower<T> {
22    pub fn new(name: T) -> Result<Self, Error> {
23        Lower::try_bytes(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 borrow(&self) -> Lower<&[u8]> {
32        Lower(self.0.as_bytes())
33    }
34}
35
36impl<T: storage::Text> Lower<T> {
37    pub fn as_str(&self) -> &str {
38        self.0.as_str()
39    }
40}
41
42impl<T> Lower<T> {
43    pub fn into_inner(self) -> T {
44        self.0
45    }
46}
47
48impl<'a> Lower<&'a [u8]> {
49    pub const fn try_bytes(name: &'a [u8]) -> Result<Self, Error> {
50        if name.is_empty() {
51            return Err(Error::Empty);
52        }
53        let mut index = 0;
54        while index < name.len() {
55            let byte = name[index];
56            if !Error::is_name_byte(byte) || byte.is_ascii_uppercase() {
57                return Err(Error::InvalidByte { index, byte });
58            }
59            index += 1;
60        }
61        Ok(Self(name))
62    }
63
64    pub const fn get(self) -> &'a [u8] {
65        self.0
66    }
67}
68
69impl<'a> Lower<Cow<'a, [u8]>> {
70    pub const fn borrowed(name: Lower<&'a [u8]>) -> Self {
71        Self(Cow::Borrowed(name.0))
72    }
73}
74
75impl<'a> TryFrom<Name<&'a [u8]>> for Lower<Cow<'a, [u8]>> {
76    type Error = alloc::Exhausted;
77
78    fn try_from(name: Name<&'a [u8]>) -> Result<Self, Self::Error> {
79        let bytes = name.0;
80        if !bytes.iter().any(u8::is_ascii_uppercase) {
81            return Ok(Self(Cow::Borrowed(bytes)));
82        }
83        let mut bytes = Box::try_copy(bytes)?.into_vec();
84        bytes.make_ascii_lowercase();
85        Ok(Self(Cow::Owned(bytes)))
86    }
87}
88
89impl<'a> Lower<Cow<'a, str>> {
90    pub const CONTENT_TYPE: Self = Self(Cow::Borrowed("content-type"));
91
92    pub fn try_name(name: Name<Cow<'a, str>>) -> Result<Self, alloc::Exhausted> {
93        Ok(Self(name.try_lower()?.0))
94    }
95
96    pub(crate) fn try_entry(entry: field::Entry<'_>) -> Result<Self, alloc::Exhausted> {
97        Ok(Self(Name::try_entry(entry)?.0))
98    }
99
100    pub fn try_copy(name: &[u8]) -> Result<Self, storage::Error> {
101        Ok(Self(Name::try_copy(name)?.0))
102    }
103}
104
105impl<T: storage::Bytes> AsRef<[u8]> for Lower<T> {
106    fn as_ref(&self) -> &[u8] {
107        self.as_bytes()
108    }
109}