base_message/field_name.rs
1// Released under the MIT License.
2// Copyright, 2026, by Samuel Williams.
3
4use crate::__private::{IncomingField, IncomingName};
5use crate::field_ref::IntoField;
6use crate::{FieldSection, StaticTableEntry};
7use std::fmt;
8
9/// How a decoder with a static table names one field for section `S`: by an entry of the table, classified for `S`, or as text when the name was sent literally.
10///
11/// ```
12/// use base_message::{FieldName, FieldSection, FieldType, FieldValueError, StaticTableEntry, ascii_text, combination::Singleton, static_table_entries};
13///
14/// struct Text(String);
15///
16/// impl FieldType<'_> for Text {
17/// type Combination = Singleton;
18/// fn parse(value: &[u8]) -> Result<Self, FieldValueError> {
19/// Ok(Self(ascii_text(value)?.to_owned()))
20/// }
21/// }
22///
23/// #[derive(FieldSection)]
24/// struct Request {
25/// #[field("host")]
26/// host: Option<Text>,
27/// #[field("x-request-id")]
28/// request_id: Option<Text>,
29/// }
30///
31/// const TABLE: [StaticTableEntry<Request>; 1] = static_table_entries::<Request, 1>(&["host"]);
32/// let fields = [(FieldName::Indexed(TABLE[0]), &b"example.com"[..]), (FieldName::Text("x-request-id"), b"abc")];
33/// let request = Request::interpret(fields).unwrap();
34/// assert_eq!(request.request_id.map(|id| id.0).as_deref(), Some("abc"));
35/// ```
36pub enum FieldName<'a, S> {
37 /// A name sent literally, to match against the section's declarations.
38 Text(&'a str),
39 /// A name sent as an index into the static table, classified for the section.
40 Indexed(StaticTableEntry<S>),
41}
42
43impl<'a, S> FieldName<'a, S> {
44 /// The name's text.
45 pub fn as_str(&self) -> &'a str {
46 match self {
47 Self::Text(name) => name,
48 Self::Indexed(entry) => entry.name(),
49 }
50 }
51}
52
53impl<S> Clone for FieldName<'_, S> {
54 fn clone(&self) -> Self {
55 *self
56 }
57}
58
59impl<S> Copy for FieldName<'_, S> {}
60
61impl<S> PartialEq for FieldName<'_, S> {
62 fn eq(&self, other: &Self) -> bool {
63 match (self, other) {
64 (Self::Text(left), Self::Text(right)) => left == right,
65 (Self::Indexed(left), Self::Indexed(right)) => left == right,
66 _ => false,
67 }
68 }
69}
70
71impl<S> Eq for FieldName<'_, S> {}
72
73impl<S> fmt::Debug for FieldName<'_, S> {
74 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
75 match self {
76 Self::Text(name) => formatter.debug_tuple("Text").field(name).finish(),
77 Self::Indexed(entry) => formatter.debug_tuple("Indexed").field(entry).finish(),
78 }
79 }
80}
81
82impl<'a, S: FieldSection<'a>> IntoField<'a, S> for (FieldName<'a, S::Identity>, &'a [u8]) {
83 fn into_field(self, index: usize) -> IncomingField<'a> {
84 let name = match self.0 {
85 FieldName::Text(name) => IncomingName::Text(name),
86 FieldName::Indexed(entry) => entry.incoming_name(),
87 };
88 IncomingField {
89 position: index,
90 name,
91 value: self.1,
92 }
93 }
94}