base_message/field_section.rs
1// Released under the MIT License.
2// Copyright, 2026, by Samuel Williams.
3
4use crate::__private::IncomingName;
5use crate::field_ref::IntoField;
6use crate::{FieldError, FieldNames, FieldRef};
7
8/// A field section interpreted into a struct. It is implemented only by `#[derive(FieldSection)]`.
9///
10/// A section takes the fields it declares, refuses those it forbids, and either ignores or rejects the rest. [`Self::interpret`] discards the fields it does not take; [`Self::partition`] returns them, borrowed and with their original positions, for another section to interpret.
11///
12/// The lifetime is that of the field values, which a section with a lifetime parameter can borrow; a section that owns its values implements `FieldSection<'a>` for every `'a`.
13pub trait FieldSection<'a>: Sized {
14 /// The names this section declares and forbids, including those of sections it flattens.
15 const NAMES: FieldNames;
16
17 /// Whether the section rejects fields it neither declares nor forbids, including because a section it flattens does.
18 #[doc(hidden)]
19 const REJECTS_UNKNOWN: bool = Self::NAMES.rejects_unknown();
20
21 /// The section's type with its lifetime, if any, as `'static`, which identifies the section to its static tables.
22 #[doc(hidden)]
23 type Identity: 'static;
24
25 #[doc(hidden)]
26 type Builder;
27
28 /// An empty builder, before any field is accepted.
29 #[doc(hidden)]
30 fn builder() -> Self::Builder;
31
32 /// Offer one field by name, reporting whether a declaration took it. A forbidden name is an error.
33 #[doc(hidden)]
34 fn accept(
35 builder: &mut Self::Builder,
36 name: &str,
37 value: &'a [u8],
38 position: usize,
39 ) -> Result<bool, FieldError>;
40
41 /// Accept a field by its declaration id, as [`FieldNames::declaration`] numbers them, without comparing names.
42 #[doc(hidden)]
43 fn accept_declaration(
44 builder: &mut Self::Builder,
45 declaration: usize,
46 value: &'a [u8],
47 position: usize,
48 ) -> Result<(), FieldError>;
49
50 /// Build the section, reporting a missing required field.
51 #[doc(hidden)]
52 fn finish(builder: Self::Builder) -> Result<Self, FieldError>;
53
54 /// Interpret a section from its fields in wire order, taking those it declares and discarding the rest.
55 ///
56 /// # Errors
57 ///
58 /// The first [`FieldError`]: a duplicate singleton, an invalid value its field treats as an error, a forbidden field, an unknown field the section rejects, or a missing required field.
59 fn interpret<I>(fields: I) -> Result<Self, FieldError>
60 where
61 I: IntoIterator,
62 I::Item: IntoField<'a, Self>,
63 {
64 interpret::<Self, I, Vec<FieldRef<'a>>>(fields, None)
65 }
66
67 /// Interpret a section from its fields in wire order, taking those it declares and returning the rest, with their original positions, for another section to interpret.
68 ///
69 /// The remainder can be any collection: a `Vec<FieldRef>` borrows the fields without copying them, and [`OwnedFields`](crate::OwnedFields) copies them. A field a static table classified as undeclared for this section is passed on by name, since the next section may declare it. A forbidden field is an error, never part of the remainder.
70 ///
71 /// # Errors
72 ///
73 /// As for [`Self::interpret`].
74 fn partition<I, R>(fields: I) -> Result<(Self, R), FieldError>
75 where
76 I: IntoIterator,
77 I::Item: IntoField<'a, Self>,
78 R: Default + Extend<FieldRef<'a>>,
79 {
80 let mut remainder = R::default();
81 let section = interpret::<Self, I, R>(fields, Some(&mut remainder))?;
82 Ok((section, remainder))
83 }
84}
85
86// SECURITY: Names are matched as given, ASCII case-insensitively: a malformed name, such as one with surrounding spaces, a NUL, or a non-ASCII lookalike letter, matches no declared or forbidden name and is treated as undeclared, so it is not refused. This is not a bypass, because protocol crates validate field-name syntax before interpretation, as `design/scope.md` requires, so such a name never arrives.
87fn interpret<'a, S, I, R>(fields: I, mut remainder: Option<&mut R>) -> Result<S, FieldError>
88where
89 S: FieldSection<'a>,
90 I: IntoIterator,
91 I::Item: IntoField<'a, S>,
92 R: Extend<FieldRef<'a>>,
93{
94 let mut builder = S::builder();
95 for (index, field) in fields.into_iter().enumerate() {
96 let field = field.into_field(index);
97 let unclaimed = match field.name {
98 IncomingName::Declaration(declaration) => {
99 S::accept_declaration(
100 &mut builder,
101 usize::from(declaration),
102 field.value,
103 field.position,
104 )?;
105 None
106 }
107 IncomingName::Forbidden(name) => {
108 return Err(FieldError::forbidden(name, field.position));
109 }
110 IncomingName::Undeclared(name) => Some(name),
111 IncomingName::Text(name) => {
112 (!S::accept(&mut builder, name, field.value, field.position)?).then_some(name)
113 }
114 };
115 if let Some(name) = unclaimed {
116 if S::REJECTS_UNKNOWN {
117 return Err(FieldError::unknown(field.position));
118 }
119 if let Some(remainder) = remainder.as_deref_mut() {
120 remainder.extend([FieldRef::new(field.position, name, field.value)]);
121 }
122 }
123 }
124 S::finish(builder)
125}