Skip to main content

flodl_cli/
context.rs

1//! Execution context: project-local vs global (~/.flodl).
2//!
3//! When running inside a floDl project (detected by walking up from cwd),
4//! libtorch operations target `./libtorch/`. When standalone, they target
5//! `~/.flodl/libtorch/`.
6
7use std::env;
8use std::fs;
9use std::path::PathBuf;
10
11pub struct Context {
12    /// Root directory for libtorch storage. Either a project root or ~/.flodl.
13    pub root: PathBuf,
14    /// Whether we are operating inside a detected project.
15    pub is_project: bool,
16}
17
18impl Context {
19    /// Auto-detect: walk up from cwd looking for a flodl project, fall back
20    /// to `~/.flodl/`.
21    pub fn resolve() -> Self {
22        if let Some(project_root) = find_project_root() {
23            Context { root: project_root, is_project: true }
24        } else {
25            Context { root: global_root(), is_project: false }
26        }
27    }
28
29    /// Force a specific root (for --path overrides).
30    pub fn with_root(root: PathBuf) -> Self {
31        let is_project = root.join("Cargo.toml").exists();
32        Context { root, is_project }
33    }
34
35    /// The libtorch directory under this root.
36    pub fn libtorch_dir(&self) -> PathBuf {
37        self.root.join("libtorch")
38    }
39
40    /// Print a short context line (for diagnostics).
41    pub fn label(&self) -> String {
42        if self.is_project {
43            format!("project ({})", self.root.display())
44        } else {
45            format!("global ({})", self.root.display())
46        }
47    }
48}
49
50/// Walk up from cwd looking for a flodl project.
51fn find_project_root() -> Option<PathBuf> {
52    let mut dir = env::current_dir().ok()?;
53    loop {
54        // Strongest signal: libtorch/.active exists here
55        if dir.join("libtorch/.active").exists() {
56            return Some(dir);
57        }
58        // Secondary signal: Cargo.toml mentioning flodl
59        let cargo_toml = dir.join("Cargo.toml");
60        if cargo_toml.exists() {
61            if let Ok(contents) = fs::read_to_string(&cargo_toml) {
62                if contents.contains("flodl") {
63                    return Some(dir);
64                }
65            }
66        }
67        if !dir.pop() {
68            return None;
69        }
70    }
71}
72
73/// Global root: $FLODL_HOME or ~/.flodl/
74fn global_root() -> PathBuf {
75    env::var("FLODL_HOME")
76        .map(PathBuf::from)
77        .unwrap_or_else(|_| home_dir().join(".flodl"))
78}
79
80fn home_dir() -> PathBuf {
81    env::var("HOME")
82        .or_else(|_| env::var("USERPROFILE"))
83        .map(PathBuf::from)
84        .unwrap_or_else(|_| PathBuf::from("."))
85}