Skip to main content

typewriter_engine/
project.rs

1//! Project root and configuration discovery utilities.
2
3use anyhow::{bail, Result};
4use std::path::{Path, PathBuf};
5use typewriter_core::config::TypewriterConfig;
6
7/// Discover project root from a starting path by searching ancestors for `Cargo.toml`.
8pub fn discover_project_root(start: &Path) -> Result<PathBuf> {
9    for ancestor in start.ancestors() {
10        if ancestor.join("Cargo.toml").exists() {
11            return Ok(ancestor.to_path_buf());
12        }
13    }
14
15    bail!(
16        "failed to locate project root from '{}': no Cargo.toml found in ancestors",
17        start.display()
18    )
19}
20
21/// Discover best root to resolve `typewriter.toml`/generated paths in proc-macro context.
22///
23/// Priority:
24/// 1) nearest ancestor containing `typewriter.toml`
25/// 2) nearest ancestor containing `Cargo.toml`
26/// 3) original manifest directory
27pub fn discover_macro_root(manifest_dir: &Path) -> PathBuf {
28    let mut cargo_root = None;
29
30    for ancestor in manifest_dir.ancestors() {
31        if ancestor.join("typewriter.toml").exists() {
32            return ancestor.to_path_buf();
33        }
34        if cargo_root.is_none() && ancestor.join("Cargo.toml").exists() {
35            cargo_root = Some(ancestor.to_path_buf());
36        }
37    }
38
39    cargo_root.unwrap_or_else(|| manifest_dir.to_path_buf())
40}
41
42pub fn load_config(project_root: &Path) -> Result<TypewriterConfig> {
43    TypewriterConfig::load(project_root)
44}
45
46pub fn load_config_or_default(project_root: &Path) -> TypewriterConfig {
47    load_config(project_root).unwrap_or_default()
48}