catchr_core/
section_body.rs1use proc_macro2::TokenStream;
2use quote::{quote, TokenStreamExt};
3use syn::parse::{self, Parse, ParseStream};
4
5use crate::catchr_mode::CatchrMode;
6use crate::scope::Scope;
7use crate::section::Section;
8use crate::section_item::SectionItem;
9use crate::section_keyword::SectionKeyword;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct SectionBody {
13 items: Vec<SectionItem>,
14}
15
16impl SectionBody {
17 pub fn with_mode(mut self, test_attribute: CatchrMode) -> Self {
18 self.items = self
19 .items
20 .into_iter()
21 .map(|item| item.with_mode(test_attribute))
22 .collect();
23
24 self
25 }
26
27 pub fn empty() -> Self {
28 Self { items: vec![] }
29 }
30
31 pub fn new(items: Vec<SectionItem>) -> Self {
32 Self { items }
33 }
34
35 fn push_stmt(&mut self, stmt: syn::Stmt) {
36 self.items.push(SectionItem::Stmt(stmt));
37 }
38
39 fn push_section(&mut self, item: Section) {
40 self.items.push(SectionItem::Sep(item));
41 }
42
43 pub fn is_top_level(&self) -> bool {
44 self.items.iter().all(|item| item.is_stmt())
45 }
46
47 pub fn get_stmts_before(&self, idx: usize) -> Vec<syn::Stmt> {
48 self.items
49 .iter()
50 .take(idx)
51 .filter_map(|i| i.stmt())
52 .collect()
53 }
54
55 pub fn get_stmts_after(&self, idx: usize) -> Vec<syn::Stmt> {
56 self.items
57 .iter()
58 .skip(idx + 1)
59 .filter_map(|i| i.stmt())
60 .collect()
61 }
62
63 pub fn items(&self) -> &[SectionItem] {
64 &self.items
65 }
66
67 pub fn to_tokens_inner(&self, mut scope: Scope, tokens: &mut TokenStream) {
68 let mut stream = vec![];
69
70 for (idx, item) in self.items.iter().enumerate() {
71 if let SectionItem::Sep(section) = item {
72 let sb = self.get_stmts_before(idx);
73 let sa = self.get_stmts_after(idx);
74
75 scope.push_mut(&sb, &sa);
76 let inner = section.quote_inner(scope.clone());
77
78 stream.push(quote! {
79 mod catchr_scenarios {
80 use super::*;
81
82 #inner
83 }
84 });
85 }
86 }
87
88 tokens.append_all(stream);
89 }
90}
91
92impl Parse for SectionBody {
93 fn parse(input: ParseStream) -> parse::Result<Self> {
94 let mut body = SectionBody { items: vec![] };
95
96 loop {
97 if SectionKeyword::peek(input) {
98 let inner_section = input.parse()?;
99
100 body.push_section(inner_section);
101 } else if input.is_empty() {
102 break;
103 } else {
104 let next = input.parse::<syn::Stmt>()?;
105 body.push_stmt(next);
106 }
107 }
108
109 Ok(body)
110 }
111}