base_web_multipart/
part_head.rs1use std::fmt;
5use std::iter::FusedIterator;
6
7#[derive(Clone, Copy, PartialEq, Eq, Hash)]
11pub struct PartHead<'a> {
12 section: &'a [u8],
14}
15
16impl<'a> PartHead<'a> {
17 pub(crate) fn new(section: &'a [u8]) -> Self {
18 Self { section }
19 }
20
21 pub fn fields(&self) -> PartFields<'a> {
23 PartFields { rest: self.section }
24 }
25
26 pub fn len(&self) -> usize {
28 self.fields().count()
29 }
30
31 pub fn is_empty(&self) -> bool {
33 self.section.is_empty()
34 }
35
36 pub fn as_bytes(&self) -> &'a [u8] {
38 self.section
39 }
40}
41
42impl fmt::Debug for PartHead<'_> {
44 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
45 let fields = self
46 .fields()
47 .map(|(name, value)| (name, format!("<{} bytes>", value.len())));
48 formatter
49 .debug_struct("PartHead")
50 .field("fields", &fields.collect::<Vec<_>>())
51 .finish()
52 }
53}
54
55impl<'a> IntoIterator for PartHead<'a> {
56 type Item = (&'a str, &'a [u8]);
57 type IntoIter = PartFields<'a>;
58
59 fn into_iter(self) -> Self::IntoIter {
60 self.fields()
61 }
62}
63
64#[derive(Clone)]
66pub struct PartFields<'a> {
67 rest: &'a [u8],
68}
69
70impl fmt::Debug for PartFields<'_> {
72 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
73 formatter
74 .debug_struct("PartFields")
75 .field("remaining", &format_args!("<{} bytes>", self.rest.len()))
76 .finish()
77 }
78}
79
80impl<'a> Iterator for PartFields<'a> {
81 type Item = (&'a str, &'a [u8]);
82
83 fn next(&mut self) -> Option<Self::Item> {
84 let end = memchr::memmem::find(self.rest, b"\r\n")?;
86 let line = &self.rest[..end];
87 self.rest = &self.rest[end + 2..];
88 let colon = memchr::memchr(b':', line).expect("a checked header line has a colon");
89 let name = std::str::from_utf8(&line[..colon]).expect("a checked field name is ASCII");
90 Some((name, base_message::trim_whitespace(&line[colon + 1..])))
91 }
92}
93
94impl FusedIterator for PartFields<'_> {}