From 0080efea9386d46f65d1862fcb90eb44999d9761 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 23 Feb 2026 11:38:42 +0900 Subject: feat(vcs): add mozart-vcs crate for VCS repository support Implement VCS driver/downloader infrastructure mirroring Composer's VCS subsystem. Includes drivers for GitHub, GitLab, Bitbucket, Forgejo, Git, Hg, and SVN with API-based metadata resolution, plus source downloaders for Git/Hg/SVN. Integrates into mozart-registry via vcs_bridge module to scan VCS repositories and feed discovered packages into the SAT resolver. Co-Authored-By: Claude Opus 4.6 --- crates/mozart-vcs/src/downloader/svn.rs | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 crates/mozart-vcs/src/downloader/svn.rs (limited to 'crates/mozart-vcs/src/downloader/svn.rs') diff --git a/crates/mozart-vcs/src/downloader/svn.rs b/crates/mozart-vcs/src/downloader/svn.rs new file mode 100644 index 0000000..5222b06 --- /dev/null +++ b/crates/mozart-vcs/src/downloader/svn.rs @@ -0,0 +1,64 @@ +use std::path::Path; + +use anyhow::Result; + +use crate::util::svn::SvnUtil; + +use super::VcsDownloader; + +/// SVN downloader using checkout/switch. +pub struct SvnDownloader { + svn_util: SvnUtil, +} + +impl SvnDownloader { + pub fn new(svn_util: SvnUtil) -> Self { + Self { svn_util } + } +} + +impl VcsDownloader for SvnDownloader { + fn download(&self, _url: &str, _reference: &str, _target: &Path) -> Result<()> { + // SVN doesn't need a pre-download step + Ok(()) + } + + fn install(&self, url: &str, reference: &str, target: &Path) -> Result<()> { + let target_str = target.to_string_lossy().to_string(); + let svn_url = format!("{url}@{reference}"); + self.svn_util + .execute(&["checkout", &svn_url, &target_str], None)?; + Ok(()) + } + + fn update(&self, url: &str, _old_ref: &str, new_ref: &str, target: &Path) -> Result<()> { + let svn_url = format!("{url}@{new_ref}"); + self.svn_util + .execute(&["switch", "--ignore-ancestry", &svn_url], Some(target))?; + Ok(()) + } + + fn remove(&self, target: &Path) -> Result<()> { + if target.exists() { + std::fs::remove_dir_all(target)?; + } + Ok(()) + } + + fn local_changes(&self, target: &Path) -> Result> { + let output = self.svn_util.execute(&["status"], Some(target))?; + if output.stdout.trim().is_empty() { + Ok(None) + } else { + Ok(Some(output.stdout)) + } + } + + fn commit_logs(&self, from: &str, to: &str, target: &Path) -> Result { + let range = format!("{from}:{to}"); + let output = self + .svn_util + .execute(&["log", "-r", &range], Some(target))?; + Ok(output.stdout) + } +} -- cgit v1.3.1