1use crate::ast::{Section, SectionType};
8
9fn is_preamble(t: &SectionType) -> bool {
11 matches!(
12 t,
13 SectionType::Address
14 | SectionType::Tls
15 | SectionType::Proto
16 | SectionType::Options
17 | SectionType::RequestHeaders
18 )
19}
20
21fn is_content_section(t: &SectionType) -> bool {
22 matches!(
23 t,
24 SectionType::Request
25 | SectionType::Response
26 | SectionType::Error
27 | SectionType::Asserts
28 | SectionType::Extract
29 )
30}
31
32pub fn split_sections_by_boundary_owned(sections: Vec<Section>) -> Vec<Vec<Section>> {
36 if sections.is_empty() {
37 return Vec::new();
38 }
39
40 let mut docs: Vec<Vec<Section>> = Vec::new();
41 let mut current: Vec<Section> = Vec::new();
42 let mut current_has_content = false;
43
44 for section in sections {
45 if section.section_type == SectionType::Endpoint && current_has_content {
46 let mut preamble: Vec<Section> = Vec::new();
47 while let Some(last) = current.last() {
48 if is_preamble(&last.section_type) {
49 if let Some(section) = current.pop() {
50 preamble.push(section);
51 } else {
52 break;
53 }
54 } else {
55 break;
56 }
57 }
58 preamble.reverse();
59 docs.push(std::mem::take(&mut current));
60 current = preamble;
61 current_has_content = false;
62 }
63
64 if is_content_section(§ion.section_type) {
65 current_has_content = true;
66 }
67 current.push(section);
68 }
69
70 if !current.is_empty() {
71 docs.push(current);
72 }
73
74 docs
75}
76
77pub fn split_sections_by_boundary(sections: &[Section]) -> Vec<Vec<Section>> {
81 split_sections_by_boundary_owned(sections.to_vec())
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use crate::ast::{Section, SectionContent, SectionType};
88
89 fn section(stype: SectionType, line: usize) -> Section {
90 Section {
91 section_type: stype,
92 content: SectionContent::Empty,
93 inline_options: Default::default(),
94 raw_content: String::new(),
95 start_line: line,
96 end_line: line,
97 attributes: Vec::new(),
98 }
99 }
100
101 #[test]
102 fn test_split_single_document() {
103 let sections = vec![
104 section(SectionType::Endpoint, 0),
105 section(SectionType::Request, 2),
106 section(SectionType::Response, 4),
107 ];
108 let docs = split_sections_by_boundary(§ions);
109 assert_eq!(docs.len(), 1);
110 assert_eq!(docs[0].len(), 3);
111 }
112
113 #[test]
114 fn test_split_two_documents_with_preamble() {
115 let sections = vec![
116 section(SectionType::Endpoint, 0),
117 section(SectionType::Request, 2),
118 section(SectionType::Response, 4),
119 section(SectionType::Address, 7), section(SectionType::Endpoint, 9),
121 section(SectionType::Request, 11),
122 section(SectionType::Response, 13),
123 ];
124 let docs = split_sections_by_boundary(§ions);
125 assert_eq!(docs.len(), 2);
126 assert_eq!(docs[0].len(), 3); assert_eq!(docs[1].len(), 4); }
129
130 #[test]
131 fn test_split_empty_input() {
132 let sections: Vec<Section> = vec![];
133 let docs = split_sections_by_boundary(§ions);
134 assert!(docs.is_empty());
135 }
136
137 #[test]
138 fn test_split_no_endpoint_single_doc() {
139 let sections = vec![
140 section(SectionType::Address, 0),
141 section(SectionType::Request, 2),
142 section(SectionType::Response, 4),
143 ];
144 let docs = split_sections_by_boundary(§ions);
145 assert_eq!(docs.len(), 1);
146 assert_eq!(docs[0].len(), 3);
147 }
148
149 #[test]
150 fn test_split_only_preamble_sections() {
151 let sections = vec![
152 section(SectionType::Address, 0),
153 section(SectionType::Tls, 2),
154 section(SectionType::Proto, 4),
155 ];
156 let docs = split_sections_by_boundary(§ions);
157 assert_eq!(docs.len(), 1);
158 assert_eq!(docs[0].len(), 3);
159 }
160
161 #[test]
162 fn test_split_preambles_without_content_no_split() {
163 let sections = vec![
165 section(SectionType::Address, 0),
166 section(SectionType::Tls, 2),
167 section(SectionType::Proto, 4),
168 section(SectionType::Options, 6),
169 section(SectionType::RequestHeaders, 8),
170 section(SectionType::Endpoint, 10),
171 section(SectionType::Request, 12),
172 section(SectionType::Response, 14),
173 ];
174 let docs = split_sections_by_boundary(§ions);
175 assert_eq!(docs.len(), 1);
177 assert_eq!(docs[0].len(), 8); }
179
180 #[test]
181 fn test_split_endpoint_at_start() {
182 let sections = vec![
183 section(SectionType::Endpoint, 0),
184 section(SectionType::Request, 2),
185 section(SectionType::Response, 4),
186 section(SectionType::Endpoint, 6),
187 section(SectionType::Request, 8),
188 ];
189 let docs = split_sections_by_boundary(§ions);
190 assert_eq!(docs.len(), 2);
191 assert_eq!(docs[0].len(), 3);
192 assert_eq!(docs[1].len(), 2);
193 }
194
195 #[test]
196 fn test_split_extract_as_terminal() {
197 let sections = vec![
198 section(SectionType::Endpoint, 0),
199 section(SectionType::Request, 2),
200 section(SectionType::Response, 4),
201 section(SectionType::Extract, 6), section(SectionType::Endpoint, 8),
203 section(SectionType::Request, 10),
204 ];
205 let docs = split_sections_by_boundary(§ions);
206 assert_eq!(docs.len(), 2);
207 }
208
209 #[test]
210 fn test_split_asserts_as_terminal() {
211 let sections = vec![
212 section(SectionType::Endpoint, 0),
213 section(SectionType::Request, 2),
214 section(SectionType::Asserts, 4), section(SectionType::Endpoint, 6),
216 section(SectionType::Request, 8),
217 ];
218 let docs = split_sections_by_boundary(§ions);
219 assert_eq!(docs.len(), 2);
220 }
221
222 #[test]
223 fn test_split_error_as_terminal() {
224 let sections = vec![
225 section(SectionType::Endpoint, 0),
226 section(SectionType::Request, 2),
227 section(SectionType::Error, 4), section(SectionType::Endpoint, 6),
229 section(SectionType::Request, 8),
230 ];
231 let docs = split_sections_by_boundary(§ions);
232 assert_eq!(docs.len(), 2);
233 }
234
235 #[test]
236 fn test_preamble_selection() {
237 let sections = vec![
238 section(SectionType::Endpoint, 0),
239 section(SectionType::Request, 2),
240 section(SectionType::Response, 4),
241 section(SectionType::Address, 6), section(SectionType::Tls, 8), section(SectionType::RequestHeaders, 10), section(SectionType::Endpoint, 12),
245 section(SectionType::Request, 14),
246 ];
247 let docs = split_sections_by_boundary(§ions);
248 assert_eq!(docs.len(), 2);
249 assert_eq!(docs[0].len(), 3);
250 assert_eq!(docs[1].len(), 5); }
252}