Skip to main content

zoi_cli/cmd/
repo.rs

1use crate::pkg::config;
2use anyhow::Result;
3use anyhow::anyhow;
4use clap::{Parser, Subcommand};
5use colored::*;
6use comfy_table::{Table, presets::UTF8_FULL};
7use std::collections::HashSet;
8
9#[derive(Parser)]
10pub struct RepoCommand {
11    #[arg(
12        short = 'y',
13        long,
14        help = "Automatically answer yes to all prompts",
15        global = true
16    )]
17    yes: bool,
18    #[command(subcommand)]
19    command: Commands,
20}
21
22#[derive(Subcommand)]
23enum Commands {
24    /// Add a repository to the configuration or clone from a git URL
25    #[command(alias = "a")]
26    Add {
27        /// The name of the repository to add or a git URL to clone
28        repo_or_url: Option<String>,
29    },
30    /// Remove a repository from the active configuration
31    #[command(alias = "rm")]
32    Remove { repo_name: String },
33    /// List repositories (active by default); use `list all` to show all
34    #[command(alias = "ls")]
35    List {
36        #[command(subcommand)]
37        which: Option<ListSub>,
38    },
39    /// Manage cloned git repositories
40    #[command(subcommand)]
41    Git(GitCommand),
42}
43
44pub fn run(args: RepoCommand) -> Result<()> {
45    let yes = args.yes;
46    match args.command {
47        Commands::Add { repo_or_url } => {
48            if let Some(val) = repo_or_url {
49                if val.starts_with("http://")
50                    || val.starts_with("https://")
51                    || val.ends_with(".git")
52                {
53                    config::clone_git_repo(&val)?;
54                } else {
55                    config::add_repo(&val)?;
56                    println!("Repository '{}' added successfully.", val.green());
57                }
58            } else if yes {
59                return Err(anyhow!(
60                    "A repository name or URL is required when using --yes."
61                ));
62            } else {
63                config::interactive_add_repo()?;
64            }
65        }
66        Commands::Remove { repo_name } => {
67            config::remove_repo(&repo_name)?;
68            println!("Repository '{}' removed successfully.", repo_name.green());
69        }
70        Commands::List { which } => match which {
71            None => run_list_active()?,
72            Some(ListSub::All) => run_list_all()?,
73        },
74        Commands::Git(cmd) => match cmd {
75            GitCommand::List => run_list_git_only()?,
76            GitCommand::Rm { repo_name } => config::remove_git_repo(&repo_name)?,
77        },
78    }
79    Ok(())
80}
81
82fn run_list_active() -> Result<()> {
83    let config = config::read_config()?;
84    if config.repos.is_empty() {
85        println!("No active repositories.");
86        return Ok(());
87    }
88
89    println!("{} Active repositories:", "::".bold().blue());
90    let mut table = Table::new();
91    table.load_preset(UTF8_FULL).set_header(vec!["Repository"]);
92    for repo in config.repos {
93        table.add_row(vec![repo]);
94    }
95    println!("{table}");
96    Ok(())
97}
98
99fn run_list_all() -> Result<()> {
100    let active_repos = config::read_config()?
101        .repos
102        .into_iter()
103        .collect::<HashSet<_>>();
104    let all_repos = config::get_all_repos()?;
105
106    println!("{} All available repositories:", "::".bold().blue());
107    let mut table = Table::new();
108    table
109        .load_preset(UTF8_FULL)
110        .set_header(vec!["Status", "Repository"]);
111
112    for repo in all_repos {
113        let status = if active_repos.contains(&repo.to_lowercase()) {
114            "Added"
115        } else {
116            ""
117        };
118        table.add_row(vec![status.to_string(), repo]);
119    }
120    println!("{table}");
121    Ok(())
122}
123
124#[derive(Subcommand)]
125enum ListSub {
126    /// Show all available repositories (active + discovered)
127    All,
128}
129
130#[derive(Subcommand)]
131enum GitCommand {
132    /// Show only cloned git repositories (~/.zoi/pkgs/git)
133    #[command(alias = "ls")]
134    List,
135    /// Remove a cloned git repository directory (~/.zoi/pkgs/git/<repo-name>)
136    Rm { repo_name: String },
137}
138
139fn run_list_git_only() -> Result<()> {
140    let repos = config::list_git_repos()?;
141    if repos.is_empty() {
142        println!("No cloned git repositories.");
143        return Ok(());
144    }
145
146    println!(
147        "{} Cloned git repositories (~/.zoi/pkgs/git):",
148        "::".bold().blue()
149    );
150    let mut table = Table::new();
151    table.load_preset(UTF8_FULL).set_header(vec!["Repository"]);
152    for repo in repos {
153        table.add_row(vec![repo]);
154    }
155    println!("{table}");
156    Ok(())
157}