1use std::path::{Path, PathBuf};
2use std::process::Command;
3
4use anyhow::{Context, Result};
5
6use pgit_core::purl::Purl;
7
8pub fn url_from_purl(purl: &Purl) -> Result<String> {
12 let ns = purl
13 .namespace
14 .as_deref()
15 .with_context(|| format!("PURL '{}' has no namespace (registry)", purl))?;
16
17 Ok(format!("https://{}.git", ns))
19}
20
21pub fn shallow_clone(
26 url: &str,
27 git_ref: Option<&str>,
28 dir_name: &str,
29 dest: &Path,
30) -> Result<PathBuf> {
31 let clone_dir = dest.join(dir_name);
32 let clone_str = clone_dir
33 .to_str()
34 .with_context(|| "clone path is not valid UTF-8")?;
35
36 let mut args: Vec<&str> = vec!["clone", "--depth", "1"];
37 if let Some(r) = git_ref {
38 args.extend_from_slice(&["--branch", r]);
39 }
40 args.push(url);
41 args.push(clone_str);
42
43 let output = Command::new("git")
44 .args(&args)
45 .env_remove("GIT_DIR")
46 .env_remove("GIT_WORK_TREE")
47 .env_remove("GIT_INDEX_FILE")
48 .env("GIT_LFS_SKIP_SMUDGE", "0")
49 .output()
50 .context("failed to run git clone")?;
51
52 if !output.status.success() {
53 let stderr = String::from_utf8_lossy(&output.stderr);
54 anyhow::bail!("git clone failed: {}", stderr.trim());
55 }
56
57 Ok(clone_dir)
58}
59
60pub fn resolve_head_sha(repo: &Path) -> Result<String> {
62 let output = Command::new("git")
63 .args(["rev-parse", "HEAD"])
64 .current_dir(repo)
65 .env_remove("GIT_DIR")
66 .output()
67 .context("failed to run git rev-parse")?;
68
69 if !output.status.success() {
70 anyhow::bail!("git rev-parse failed");
71 }
72
73 Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
74}
75
76pub fn resolve_subfolder(clone_dir: &Path, subfolder: Option<&str>) -> Result<PathBuf> {
78 match subfolder {
79 None => Ok(clone_dir.to_path_buf()),
80 Some(sub) => {
81 let path = clone_dir.join(sub);
82 anyhow::ensure!(
83 path.is_dir(),
84 "subfolder '{}' not found in cloned repo",
85 sub
86 );
87 Ok(path)
88 }
89 }
90}
91
92pub fn cleanup(path: &Path) {
94 let _ = std::fs::remove_dir_all(path);
95}