Skip to main content

eml_codec/part/
field.rs

1use bounded_static::ToStatic;
2#[cfg(feature = "tracing")]
3use tracing::warn;
4
5#[cfg(feature = "arbitrary")]
6use crate::fuzz_eq::FuzzEq;
7use crate::header;
8use crate::mime;
9use crate::print::{Formatter, Print};
10use crate::raw_input::RawInput;
11
12/// Header field of a generic MIME entity (a MIME entity that is not a toplevel
13/// message). Is either a MIME-defined field or an unstructured field.
14#[derive(Clone, Debug, PartialEq, ToStatic)]
15#[cfg_attr(feature = "arbitrary", derive(FuzzEq))]
16pub enum EntityField<'a> {
17    MIME {
18        f: mime::field::Field<'a>,
19        raw_body: RawInput<'a>,
20    },
21    Unstructured(header::Unstructured<'a>),
22}
23
24impl<'a> EntityField<'a> {
25    pub fn raw_name(&self) -> header::FieldName<'a> {
26        match self {
27            EntityField::MIME { f, .. } => f.raw_name(),
28            EntityField::Unstructured(u) => u.name.clone(),
29        }
30    }
31
32    pub fn raw_body(&self) -> RawInput<'a> {
33        match self {
34            EntityField::MIME { raw_body, .. } => raw_body.clone(),
35            EntityField::Unstructured(u) => u.raw_body.clone(),
36        }
37    }
38}
39
40impl<'a> Print for EntityField<'a> {
41    fn print(&self, fmt: &mut impl Formatter) {
42        match self {
43            EntityField::MIME { f, .. } => f.print(fmt),
44            EntityField::Unstructured(u) => u.print(fmt),
45        }
46    }
47}
48
49/// Entry for an entity field.
50#[derive(Clone, Debug, PartialEq, ToStatic)]
51#[cfg_attr(feature = "arbitrary", derive(FuzzEq))]
52pub enum EntityEntry<'a> {
53    MIME {
54        e: mime::field::Entry,
55        raw_body: RawInput<'a>,
56    },
57    Unstructured(header::Unstructured<'a>),
58}
59
60/// Collects fields and entries for a generic MIME entity. Only for eml-codec's
61/// internal use.
62#[derive(Debug, Default, PartialEq, ToStatic)]
63#[cfg_attr(feature = "arbitrary", derive(FuzzEq))]
64pub(crate) struct NaiveEntityFields<'a> {
65    pub mime: mime::NaiveMIME<'a>,
66    pub entries: Vec<EntityEntry<'a>>,
67}
68
69impl<'a> FromIterator<header::FieldRaw<'a>> for NaiveEntityFields<'a> {
70    #[cfg_attr(
71        feature = "tracing",
72        tracing::instrument(name = "EntityFields::from_iter", skip(it))
73    )]
74    fn from_iter<I: IntoIterator<Item = header::FieldRaw<'a>>>(it: I) -> Self {
75        let mut e: NaiveEntityFields<'a> = Default::default();
76        for f in it {
77            match mime::field::NaiveField::try_from(&f) {
78                Ok(mimef) => {
79                    if let Some(entry) = e.mime.add_field(mimef) {
80                        e.entries.push(EntityEntry::MIME {
81                            e: entry,
82                            raw_body: f.body.into(),
83                        })
84                    } else {
85                        // otherwise drop the field
86                        #[cfg(feature = "tracing-recover")]
87                        warn!(field = ?f, "dropping conflicting MIME field");
88                    }
89                    continue;
90                }
91                Err(mime::field::InvalidField::Body) => {
92                    // this is a MIME field but its body is invalid; drop it.
93                    #[cfg(feature = "tracing-unsupported")]
94                    warn!(field = ?f, "dropping MIME field with an invalid body");
95                    continue;
96                }
97                Err(mime::field::InvalidField::Name) => {
98                    // not a MIME field
99                }
100            };
101
102            if let Some(u) = header::Unstructured::from_raw(&f) {
103                e.entries.push(EntityEntry::Unstructured(u));
104            } else {
105                // otherwise drop the field
106                #[cfg(feature = "tracing-unsupported")]
107                warn!(field = ?f, "dropping field which cannot be parsed as unstructured");
108            }
109        }
110        e
111    }
112}