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