use std::cell::RefCell;
use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
use proc_macro2::Ident;
use syn::{Attribute, Item, ItemMod};
use syn::visit::Visit;
use crate::Crate;
use crate::context::{GlobalContext, ScopeChain};
use crate::{Config, error, print_phase};
use crate::tree::{CrateTree, ScopeTreeExportItem, Visitor};
pub struct FileTreeProcessor {
pub path: PathBuf,
pub scope: ScopeChain,
pub context: Rc<RefCell<GlobalContext>>,
pub attrs: Vec<Attribute>
}
impl FileTreeProcessor {
#[allow(unused)]
pub fn build(config: &Config) -> Result<CrateTree, error::Error> {
let Config { current_crate, external_crates, .. } = config;
let context = Rc::new(RefCell::new(GlobalContext::from(config)));
print_phase!("PHASE 0: PROCESS CRATES", "{}", config);
process_crates(external_crates, &context)
.and_then(|external_crates|
current_crate.process(vec![], &context)
.and_then(|current_tree| CrateTree::new(current_crate, current_tree, external_crates)))
}
pub fn process_crate_tree(crate_config: &Crate, attrs: Vec<Attribute>, context: &Rc<RefCell<GlobalContext>>) -> Result<ScopeTreeExportItem, error::Error> {
let path = crate_config.root_path();
let scope = ScopeChain::crate_root_with_ident(crate_config.ident(), attrs.clone());
Self::new(path, scope, attrs, context)
.process()
.map(Visitor::into_code_tree)
}
fn new(path: PathBuf, scope: ScopeChain, attrs: Vec<Attribute>, context: &Rc<RefCell<GlobalContext>>) -> Self {
Self { path, scope, context: context.clone(), attrs }
}
fn process(self) -> Result<Visitor, error::Error> {
self.read_syntax_tree()
.map(|syntax_tree| self.setup_visitor(syntax_tree))
}
fn read_syntax_tree(&self) -> Result<syn::File, error::Error> {
std::fs::read_to_string(&self.path)
.map_err(error::Error::from)
.and_then(|content| syn::parse_file(&content)
.map_err(error::Error::from))
}
fn to_inner_visitors(&self, items: Vec<Item>) -> Vec<Visitor> {
let mut visitors = vec![];
for item in items {
if let Item::Mod(ItemMod { ident, attrs, content: None, .. }) = item {
if !self.is_fermented_mod(&ident) {
if let Ok(visitor) = self.process_module(&ident, attrs) {
visitors.push(visitor);
}
}
}
}
visitors
}
fn setup_visitor(&self, syntax_tree: syn::File) -> Visitor {
let mut visitor = Visitor::new(&self.scope, &self.attrs, &self.context);
visitor.visit_file(&syntax_tree);
visitor.inner_visitors = self.to_inner_visitors(syntax_tree.items);
visitor
}
fn process_module(&self, mod_name: &Ident, attrs: Vec<Attribute>) -> Result<Visitor, error::Error> {
let scope = ScopeChain::child_mod(attrs.clone(), self.scope.crate_ident_ref().clone(), mod_name, &self.scope);
if let Some(parent_path) = self.path.parent() {
let file_path = parent_path.join(mod_name.to_string());
if file_path.is_file() {
return FileTreeProcessor::new(file_path, scope, attrs, &self.context).process();
} else {
let path = file_path.join("mod.rs");
if path.is_file() {
return FileTreeProcessor::new(path, scope, attrs, &self.context).process()
} else if let Some(file_parent) = file_path.parent() {
let path = file_parent.join(format!("{mod_name}.rs"));
if path.is_file() {
return FileTreeProcessor::new(path, scope, attrs, &self.context).process()
}
}
}
}
Err(error::Error::ExpansionError("Can't locate module file"))
}
fn is_fermented_mod(&self, ident: &Ident) -> bool {
let lock = self.context.borrow();
lock.is_fermented_mod(ident)
}
}
#[allow(unused)]
fn process_crates(crates: &[Crate], context: &Rc<RefCell<GlobalContext>>) -> Result<HashMap<Crate, ScopeTreeExportItem>, error::Error> {
crates.iter()
.try_fold(HashMap::new(), |mut acc, crate_config| {
acc.insert(crate_config.clone(), crate_config.process(vec![], context)?);
Ok(acc)
})
}