catchr_core/
section_body.rs

1use 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}