Skip to main content

mars_agents/source/
mod.rs

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