codama_stores/
file_module_store.rs

1use codama_errors::{CodamaResult, IteratorCombineErrors};
2use std::path::{Path, PathBuf};
3
4#[derive(Debug, PartialEq)]
5pub struct FileModuleStore {
6    pub file: syn::File,
7    pub file_modules: Vec<FileModuleStore>,
8    pub path: PathBuf,
9}
10
11impl FileModuleStore {
12    pub fn load_all(path: &Path, items: &[syn::Item]) -> CodamaResult<Vec<Self>> {
13        find_nested_file_modules(items)
14            .iter()
15            .map(|&item| FileModuleStore::load(path, item))
16            .collect_and_combine_errors()
17    }
18
19    pub fn load(path: &Path, item: &syn::ItemMod) -> CodamaResult<Self> {
20        let parent_directory = path.parent().unwrap();
21        let filename = path.file_stem().unwrap().to_str().unwrap();
22        let current_directory = parent_directory.join(filename);
23
24        let candidates = vec![
25            // If we are in a mod.rs or lib.rs file, the modules will be in a sibling directory.
26            parent_directory.join(format!("{}.rs", item.ident)),
27            parent_directory.join(format!("{}/mod.rs", item.ident)),
28            // Otherwise, the modules will be in a child directory.
29            current_directory.join(format!("{}.rs", item.ident)),
30            current_directory.join(format!("{}/mod.rs", item.ident)),
31        ];
32
33        let path = candidates
34            .into_iter()
35            .find(|p| p.exists())
36            .ok_or_else(|| syn::Error::new_spanned(item, "could not read file"))?;
37        let content = std::fs::read_to_string(&path)?;
38        let file = syn::parse_file(&content)?;
39        let modules = Self::load_all(&path, &file.items)?;
40
41        Ok(Self {
42            file,
43            file_modules: modules,
44            path,
45        })
46    }
47}
48
49fn find_nested_file_modules(items: &[syn::Item]) -> Vec<&syn::ItemMod> {
50    items
51        .iter()
52        .filter_map(|item| match item {
53            syn::Item::Mod(syn::ItemMod {
54                content: Some((_, items)),
55                ..
56            }) => Some(find_nested_file_modules(items)),
57            syn::Item::Mod(item_mod) => Some(vec![item_mod]),
58            _ => None,
59        })
60        .flatten()
61        .collect()
62}