codama_stores/
crate_store.rs1use cargo_toml::Manifest;
2use codama_errors::CodamaResult;
3use std::{
4 fs,
5 path::{Path, PathBuf},
6};
7
8use crate::FileModuleStore;
9
10#[derive(Debug, PartialEq)]
11pub struct CrateStore {
12 pub file: syn::File,
13 pub manifest: Option<Manifest>,
14 pub file_modules: Vec<FileModuleStore>,
15 pub path: PathBuf,
16}
17
18impl CrateStore {
19 pub fn load<P: AsRef<Path>>(path: P) -> CodamaResult<Self> {
20 let manifest_path = get_closest_manifest_path(path.as_ref())?;
22 let mut manifest = Manifest::from_path(&manifest_path)?;
23 manifest.complete_from_path(path.as_ref())?;
24
25 let relative_product_path = get_product_path(&manifest)?;
27 let product_path = manifest_path.parent().unwrap().join(relative_product_path);
28
29 let content = fs::read_to_string(&product_path)?;
31 let file = syn::parse_file(&content)?;
32
33 let modules = FileModuleStore::load_all(&product_path, &file.items)?;
35
36 Ok(Self {
37 file,
38 manifest: Some(manifest),
39 file_modules: modules,
40 path: product_path.to_path_buf(),
41 })
42 }
43
44 pub fn hydrate(tt: proc_macro2::TokenStream) -> CodamaResult<Self> {
45 Ok(Self {
46 file: syn::parse2::<syn::File>(tt)?,
47 manifest: None,
48 file_modules: Vec::new(),
49 path: PathBuf::new(),
50 })
51 }
52}
53
54pub fn get_closest_manifest_path<P: AsRef<Path>>(path: P) -> CodamaResult<PathBuf> {
59 let mut current_path = path.as_ref().canonicalize()?;
60
61 if current_path.ends_with("Cargo.toml") && current_path.is_file() {
63 return Ok(current_path);
64 }
65
66 loop {
68 let cargo_toml = current_path.join("Cargo.toml");
69 if cargo_toml.is_file() {
70 return Ok(cargo_toml);
71 }
72
73 match current_path.parent() {
75 Some(parent) => current_path = parent.to_path_buf(),
76 None => break, }
78 }
79
80 Err(std::io::Error::new(std::io::ErrorKind::NotFound, "Cargo.toml not found").into())
82}
83
84fn get_product_path(manifest: &Manifest) -> CodamaResult<PathBuf> {
85 let product = get_product_candidates(manifest)
86 .iter()
87 .filter_map(|product| product.path.as_ref())
88 .next();
89
90 match product {
91 Some(path) => Ok(PathBuf::from(path)),
92 None => Err(cargo_toml::Error::Other("No crate path found in Cargo.toml").into()),
93 }
94}
95
96fn get_product_candidates(manifest: &Manifest) -> Vec<&cargo_toml::Product> {
97 let mut candidates = Vec::new();
98 if let Some(product) = &manifest.lib {
99 candidates.push(product);
100 }
101 for product in &manifest.bin {
102 candidates.push(product);
103 }
104 candidates
105}