1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
use proc_macro2::TokenStream;
use quote::{quote, TokenStreamExt};
use syn::parse::{self, Parse, ParseStream};

use crate::scope::Scope;
use crate::section::Section;
use crate::section_item::SectionItem;
use crate::section_keyword::SectionKeyword;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SectionBody {
    items: Vec<SectionItem>,
}

impl SectionBody {
    pub fn empty() -> Self {
        Self { items: vec![] }
    }

    pub fn new(items: Vec<SectionItem>) -> Self {
        Self { items }
    }

    fn push_stmt(&mut self, stmt: syn::Stmt) {
        self.items.push(SectionItem::Stmt(stmt));
    }

    fn push_section(&mut self, item: Section) {
        self.items.push(SectionItem::Sep(item));
    }

    pub fn is_top_level(&self) -> bool {
        self.items.iter().all(|item| item.is_stmt())
    }

    pub fn get_stmts_before(&self, idx: usize) -> Vec<syn::Stmt> {
        self.items
            .iter()
            .take(idx)
            .filter_map(|i| i.stmt())
            .collect()
    }

    pub fn get_stmts_after(&self, idx: usize) -> Vec<syn::Stmt> {
        self.items
            .iter()
            .skip(idx + 1)
            .filter_map(|i| i.stmt())
            .collect()
    }

    pub fn items(&self) -> &[SectionItem] {
        &self.items
    }

    pub fn to_tokens_inner(&self, mut scope: Scope, tokens: &mut TokenStream) {
        let mut stream = vec![];

        for (idx, item) in self.items.iter().enumerate() {
            if let SectionItem::Sep(section) = item {
                let sb = self.get_stmts_before(idx);
                let sa = self.get_stmts_after(idx);

                scope.push_mut(&sb, &sa);
                let inner = section.quote_inner(scope.clone());

                stream.push(quote! {
                    mod catchr_scenarios {
                        use super::*;

                        #inner
                    }
                });
            }
        }

        tokens.append_all(stream);
    }
}

impl Parse for SectionBody {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        let mut body = SectionBody { items: vec![] };

        loop {
            if SectionKeyword::peek(input) {
                let inner_section = input.parse()?;

                body.push_section(inner_section);
            } else if input.is_empty() {
                break;
            } else {
                let next = input.parse::<syn::Stmt>()?;
                body.push_stmt(next);
            }
        }

        Ok(body)
    }
}