Skip to main content

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}