From 24bb31c109332ae982b7091ffcd5183442ce6f6f Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 10 May 2026 20:01:21 +0900 Subject: refactor(vcs): move VCS drivers under repository module Mirror Composer's structure where VCS drivers live under Repository/Vcs/. Rename VcsDriver trait to VcsDriverInterface and BitbucketDriver to GitBitbucketDriver to match Composer naming, and add stubs for FossilDriver, PerforceDriver, and the VcsDriver base. --- crates/mozart-core/src/repository/vcs.rs | 284 +++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 crates/mozart-core/src/repository/vcs.rs (limited to 'crates/mozart-core/src/repository/vcs.rs') diff --git a/crates/mozart-core/src/repository/vcs.rs b/crates/mozart-core/src/repository/vcs.rs new file mode 100644 index 0000000..88ed702 --- /dev/null +++ b/crates/mozart-core/src/repository/vcs.rs @@ -0,0 +1,284 @@ +mod forgejo_driver; +mod fossil_driver; +mod git_bitbucket_driver; +mod git_driver; +mod github_driver; +mod gitlab_driver; +mod hg_driver; +mod perforce_driver; +mod svn_driver; +mod vcs_driver; +mod vcs_driver_interface; + +pub use forgejo_driver::*; +pub use fossil_driver::*; +pub use git_bitbucket_driver::*; +pub use git_driver::*; +pub use github_driver::*; +pub use gitlab_driver::*; +pub use hg_driver::*; +pub use perforce_driver::*; +pub use svn_driver::*; +pub use vcs_driver::*; +pub use vcs_driver_interface::*; + +use std::collections::BTreeMap; +use std::path::PathBuf; + +use anyhow::Result; +use serde::{Deserialize, Serialize}; + +/// Reference to a source distribution. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SourceReference { + #[serde(rename = "type")] + pub source_type: String, + pub url: String, + pub reference: String, +} + +/// Reference to a dist (archive) distribution. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DistReference { + #[serde(rename = "type")] + pub dist_type: String, + pub url: String, + pub reference: String, + pub shasum: Option, +} + +/// Configuration passed to VCS drivers. +#[derive(Debug, Clone)] +pub struct DriverConfig { + /// Composer's `cache-vcs-dir`: root for VCS mirrors, one + /// subdirectory per sanitized repository URL. + pub cache_vcs_dir: PathBuf, + /// GitHub OAuth token (from `GITHUB_TOKEN` or config). + pub github_token: Option, + /// GitLab OAuth token. + pub gitlab_token: Option, + /// Bitbucket OAuth consumer key/secret. + pub bitbucket_oauth: Option<(String, String)>, + /// Forgejo token. + pub forgejo_token: Option, + /// Custom GitLab domains (for self-hosted). + pub gitlab_domains: Vec, + /// Custom Forgejo domains (for self-hosted). + pub forgejo_domains: Vec, +} + +impl Default for DriverConfig { + fn default() -> Self { + Self { + cache_vcs_dir: default_cache_vcs_dir(), + github_token: None, + gitlab_token: None, + bitbucket_oauth: None, + forgejo_token: None, + gitlab_domains: vec!["gitlab.com".to_string()], + forgejo_domains: vec!["codeberg.org".to_string()], + } + } +} + +/// Resolve the default `cache-vcs-dir`, honoring Composer's env vars. +/// +/// Priority: `COMPOSER_CACHE_VCS_DIR` → `COMPOSER_CACHE_DIR/vcs` → +/// `XDG_CACHE_HOME/mozart/vcs` → `$HOME/.cache/mozart/vcs`. +fn default_cache_vcs_dir() -> PathBuf { + if let Ok(p) = std::env::var("COMPOSER_CACHE_VCS_DIR") { + return PathBuf::from(p); + } + let base = if let Ok(p) = std::env::var("COMPOSER_CACHE_DIR") { + PathBuf::from(p) + } else if let Ok(xdg) = std::env::var("XDG_CACHE_HOME") { + PathBuf::from(xdg).join("mozart") + } else if let Ok(home) = std::env::var("HOME") { + PathBuf::from(home).join(".cache").join("mozart") + } else { + PathBuf::from("/tmp").join("mozart") + }; + base.join("vcs") +} + +/// Type of VCS driver. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DriverType { + GitHub, + GitLab, + Bitbucket, + Forgejo, + Git, + Svn, + Hg, +} + +/// Enum-dispatched VCS driver. +/// +/// Wraps all concrete driver types to allow static dispatch with async trait methods. +pub enum AnyVcsDriver { + GitHub(GitHubDriver), + GitLab(GitLabDriver), + Bitbucket(GitBitbucketDriver), + Forgejo(ForgejoDriver), + Git(GitDriver), + Svn(SvnDriver), + Hg(HgDriver), +} + +macro_rules! dispatch { + ($self:expr, $method:ident $(, $arg:expr)*) => { + match $self { + AnyVcsDriver::GitHub(d) => d.$method($($arg),*), + AnyVcsDriver::GitLab(d) => d.$method($($arg),*), + AnyVcsDriver::Bitbucket(d) => d.$method($($arg),*), + AnyVcsDriver::Forgejo(d) => d.$method($($arg),*), + AnyVcsDriver::Git(d) => d.$method($($arg),*), + AnyVcsDriver::Svn(d) => d.$method($($arg),*), + AnyVcsDriver::Hg(d) => d.$method($($arg),*), + } + }; +} + +macro_rules! dispatch_async { + ($self:expr, $method:ident $(, $arg:expr)*) => { + match $self { + AnyVcsDriver::GitHub(d) => d.$method($($arg),*).await, + AnyVcsDriver::GitLab(d) => d.$method($($arg),*).await, + AnyVcsDriver::Bitbucket(d) => d.$method($($arg),*).await, + AnyVcsDriver::Forgejo(d) => d.$method($($arg),*).await, + AnyVcsDriver::Git(d) => d.$method($($arg),*).await, + AnyVcsDriver::Svn(d) => d.$method($($arg),*).await, + AnyVcsDriver::Hg(d) => d.$method($($arg),*).await, + } + }; +} + +impl AnyVcsDriver { + pub async fn initialize(&mut self) -> Result<()> { + dispatch_async!(self, initialize) + } + + pub fn root_identifier(&self) -> &str { + dispatch!(self, root_identifier) + } + + pub async fn branches(&mut self) -> Result<&BTreeMap> { + dispatch_async!(self, branches) + } + + pub async fn tags(&mut self) -> Result<&BTreeMap> { + dispatch_async!(self, tags) + } + + pub async fn composer_information( + &mut self, + identifier: &str, + ) -> Result> { + dispatch_async!(self, composer_information, identifier) + } + + pub async fn file_content(&self, file: &str, identifier: &str) -> Result> { + dispatch_async!(self, file_content, file, identifier) + } + + pub async fn change_date(&self, identifier: &str) -> Result> { + dispatch_async!(self, change_date, identifier) + } + + pub async fn dist(&self, identifier: &str) -> Result> { + dispatch_async!(self, dist, identifier) + } + + pub fn source(&self, identifier: &str) -> SourceReference { + dispatch!(self, source, identifier) + } + + pub fn url(&self) -> &str { + dispatch!(self, url) + } + + pub async fn cleanup(&mut self) -> Result<()> { + dispatch_async!(self, cleanup) + } +} + +/// Detect which driver type should handle a given URL. +/// +/// Priority order matches Composer: +/// 1. GitHub → 2. GitLab → 3. Bitbucket → 4. Forgejo → 5. Git → 6. Hg → 7. SVN +pub fn detect_driver( + url: &str, + forced_type: Option<&str>, + config: &DriverConfig, +) -> Option { + if let Some(t) = forced_type { + return match t { + "github" => Some(DriverType::GitHub), + "gitlab" => Some(DriverType::GitLab), + "bitbucket" => Some(DriverType::Bitbucket), + "forgejo" => Some(DriverType::Forgejo), + "git" => Some(DriverType::Git), + "svn" => Some(DriverType::Svn), + "hg" | "mercurial" => Some(DriverType::Hg), + _ => None, + }; + } + + let url_lower = url.to_lowercase(); + + // GitHub + if GitHubDriver::supports(url) { + return Some(DriverType::GitHub); + } + + // GitLab + if GitLabDriver::supports(url, &config.gitlab_domains) { + return Some(DriverType::GitLab); + } + + // Bitbucket + if GitBitbucketDriver::supports(url) { + return Some(DriverType::Bitbucket); + } + + // Forgejo + if ForgejoDriver::supports(url, &config.forgejo_domains) { + return Some(DriverType::Forgejo); + } + + // Git + if GitDriver::supports(url) { + return Some(DriverType::Git); + } + + // Hg + if HgDriver::supports(url) { + return Some(DriverType::Hg); + } + + // SVN + if url_lower.contains("svn") || SvnDriver::supports(url) { + return Some(DriverType::Svn); + } + + // Default to git for generic URLs + if url.starts_with("http://") || url.starts_with("https://") { + return Some(DriverType::Git); + } + + None +} + +/// Create a driver instance for the given URL and type. +pub fn create_driver(url: &str, driver_type: DriverType, config: DriverConfig) -> AnyVcsDriver { + match driver_type { + DriverType::GitHub => AnyVcsDriver::GitHub(GitHubDriver::new(url, config)), + DriverType::GitLab => AnyVcsDriver::GitLab(GitLabDriver::new(url, config)), + DriverType::Bitbucket => AnyVcsDriver::Bitbucket(GitBitbucketDriver::new(url, config)), + DriverType::Forgejo => AnyVcsDriver::Forgejo(ForgejoDriver::new(url, config)), + DriverType::Git => AnyVcsDriver::Git(GitDriver::new(url, config)), + DriverType::Svn => AnyVcsDriver::Svn(SvnDriver::new(url, config)), + DriverType::Hg => AnyVcsDriver::Hg(HgDriver::new(url, config)), + } +} -- cgit v1.3.1