use crate::__private::{IncomingField, IncomingName};
use crate::FieldSection;
use crate::field_ref::IntoField;
use std::fmt;
use std::marker::PhantomData;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Classification {
Declaration(u16),
Forbidden,
Undeclared,
}
pub struct StaticTableEntry<S> {
classification: Classification,
name: &'static str,
section: PhantomData<fn() -> S>,
}
impl<S> StaticTableEntry<S> {
pub const fn classification(&self) -> Classification {
self.classification
}
pub const fn name(&self) -> &'static str {
self.name
}
pub(crate) fn incoming_name(self) -> IncomingName<'static> {
match self.classification {
Classification::Declaration(declaration) => IncomingName::Declaration(declaration),
Classification::Forbidden => IncomingName::Forbidden(self.name),
Classification::Undeclared => IncomingName::Undeclared(self.name),
}
}
}
impl<S> Clone for StaticTableEntry<S> {
fn clone(&self) -> Self {
*self
}
}
impl<S> Copy for StaticTableEntry<S> {}
impl<S> PartialEq for StaticTableEntry<S> {
fn eq(&self, other: &Self) -> bool {
(self.classification, self.name) == (other.classification, other.name)
}
}
impl<S> Eq for StaticTableEntry<S> {}
impl<S> fmt::Debug for StaticTableEntry<S> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("StaticTableEntry")
.field("classification", &self.classification)
.field("name", &self.name)
.finish()
}
}
impl<'a, S: FieldSection<'a>> IntoField<'a, S> for (StaticTableEntry<S::Identity>, &'a [u8]) {
fn into_field(self, index: usize) -> IncomingField<'a> {
IncomingField {
position: index,
name: self.0.incoming_name(),
value: self.1,
}
}
}
pub const fn static_table_entries<S: FieldSection<'static>, const N: usize>(
table: &[&'static str; N],
) -> [StaticTableEntry<S::Identity>; N] {
let mut entries = [StaticTableEntry {
classification: Classification::Undeclared,
name: "",
section: PhantomData,
}; N];
let mut index = 0;
while index < N {
assert!(
lowercase(table[index]),
"a static table's names must be lowercase visible ASCII, as HPACK's and QPACK's are"
);
let classification = if let Some(declaration) = S::NAMES.declaration(table[index]) {
assert!(
declaration <= u16::MAX as usize,
"a static table classifies declaration ids up to 65,535"
);
#[allow(
clippy::cast_possible_truncation,
reason = "the assertion above bounds the id"
)]
Classification::Declaration(declaration as u16)
} else if S::NAMES.forbids(table[index]) {
Classification::Forbidden
} else {
Classification::Undeclared
};
entries[index] = StaticTableEntry {
classification,
name: table[index],
section: PhantomData,
};
index += 1;
}
entries
}
const fn lowercase(name: &str) -> bool {
let bytes = name.as_bytes();
let mut index = 0;
while index < bytes.len() {
if !bytes[index].is_ascii_graphic() || bytes[index].is_ascii_uppercase() {
return false;
}
index += 1;
}
!bytes.is_empty()
}