Skip to main content

zoi_cli/cmd/
clone.rs

1//! Implementation of the `clone` command for cloning a package's git
2//! repository.
3
4use std::process::Command;
5
6use anyhow::{Result, anyhow};
7use colored::Colorize;
8
9use crate::pkg::{resolve, types};
10
11/// Runs the `clone` command to clone a package's git repository.
12///
13/// # Errors
14///
15/// Returns an error if the package cannot be resolved, if its metadata cannot
16/// be parsed, if it has no git repository defined, or if the `git clone`
17/// command fails.
18///
19/// # Panics
20///
21/// This function does not explicitly panic.
22pub fn run(
23    package_name: &str,
24    location: Option<String>,
25    yes: bool
26) -> Result<()> {
27    println!(
28        "{} Resolving package '{}' for cloning...",
29        "::".bold().blue(),
30        package_name.cyan().bold()
31    );
32
33    let resolved_source =
34        resolve::resolve_source(package_name, None, false, yes)?;
35
36    let pkg: types::Package = crate::pkg::lua::parser::parse_lua_package(
37        resolved_source.path.to_str().ok_or_else(|| {
38            let p = resolved_source.path.display();
39            anyhow!("Path contains invalid UTF-8 characters: {p}")
40        })?,
41        None,
42        None,
43        false
44    )?;
45
46    if pkg.git.is_empty() {
47        return Err(anyhow!(
48            "Package '{}' does not have a git repository defined in its \
49             metadata.",
50            pkg.name
51        ));
52    }
53
54    let git_url = &pkg.git;
55    let target_location = location.unwrap_or(pkg.name);
56
57    println!(
58        "{} Cloning {} into {}...",
59        "::".bold().blue(),
60        git_url.cyan(),
61        if target_location == "." {
62            "current directory".bold()
63        } else {
64            target_location.bold()
65        }
66    );
67
68    let mut git_cmd = Command::new("git");
69    git_cmd.arg("clone").arg(git_url).arg(&target_location);
70
71    let status = git_cmd
72        .status()
73        .map_err(|e| anyhow!("Failed to execute git clone: {e}"))?;
74
75    if status.success() {
76        println!("\n{}", "Successfully cloned repository.".green());
77    } else {
78        let code = status
79            .code()
80            .map_or_else(|| "unknown".to_string(), |c| c.to_string());
81        return Err(anyhow!("git clone failed with exit code {code}"));
82    }
83
84    Ok(())
85}