Skip to main content

mars_agents/source/
mod.rs

1pub(crate) mod archive;
2pub mod git;
3pub(crate) mod git_cli;
4pub mod parse;
5pub mod path;
6
7use std::path::PathBuf;
8
9use crate::error::MarsError;
10use crate::types::{CommitHash, SourceName, SourceUrl};
11
12/// Global source cache under `~/.mars/cache` (or `MARS_CACHE_DIR`).
13#[derive(Debug, Clone)]
14pub struct GlobalCache {
15    pub root: PathBuf,
16}
17
18impl GlobalCache {
19    /// Create a new cache directory, ensuring required subdirs exist.
20    ///
21    /// Resolution order:
22    /// 1. `MARS_CACHE_DIR`
23    /// 2. `dirs::home_dir()/.mars/cache`
24    /// 3. `{current_working_dir}/.mars/cache` fallback
25    pub fn new() -> Result<Self, MarsError> {
26        let root = if let Some(cache_dir) = std::env::var_os("MARS_CACHE_DIR") {
27            PathBuf::from(cache_dir)
28        } else if let Some(home) = dirs::home_dir() {
29            home.join(".mars").join("cache")
30        } else {
31            std::env::current_dir()
32                .unwrap_or_else(|_| PathBuf::from("."))
33                .join(".mars")
34                .join("cache")
35        };
36
37        let cache = Self { root };
38        std::fs::create_dir_all(cache.archives_dir())?;
39        std::fs::create_dir_all(cache.git_dir())?;
40        Ok(cache)
41    }
42
43    pub fn archives_dir(&self) -> PathBuf {
44        self.root.join("archives")
45    }
46
47    pub fn git_dir(&self) -> PathBuf {
48        self.root.join("git")
49    }
50}
51
52/// A resolved source reference — pinned to a specific version/commit.
53#[derive(Debug, Clone)]
54pub struct ResolvedRef {
55    pub source_name: SourceName,
56    pub version: Option<semver::Version>,
57    /// Original tag name (e.g., "v0.5.2")
58    pub version_tag: Option<String>,
59    pub commit: Option<CommitHash>,
60    pub tree_path: PathBuf,
61}
62
63/// Available version from a git remote.
64#[derive(Debug, Clone)]
65pub struct AvailableVersion {
66    pub tag: String,
67    pub version: semver::Version,
68    pub commit_id: String,
69}
70
71/// List available versions from a git remote (for resolution).
72pub fn list_versions(
73    url: &SourceUrl,
74    cache: &GlobalCache,
75) -> Result<Vec<AvailableVersion>, MarsError> {
76    git::list_versions(url.as_ref(), cache)
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn global_cache_creates_directory() {
85        let cache = GlobalCache::new().unwrap();
86        assert!(cache.root.exists());
87        if let Some(from_env) = std::env::var_os("MARS_CACHE_DIR") {
88            assert_eq!(cache.root, PathBuf::from(from_env));
89        } else {
90            assert!(cache.root.ends_with(".mars/cache"));
91        }
92        assert!(cache.archives_dir().exists());
93        assert!(cache.git_dir().exists());
94    }
95
96    #[test]
97    fn global_cache_idempotent() {
98        let cache1 = GlobalCache::new().unwrap();
99        let cache2 = GlobalCache::new().unwrap();
100        assert_eq!(cache1.root, cache2.root);
101        assert!(cache1.root.exists());
102    }
103}