zoi/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    let mut table = Table::new();
90    table
91        .load_preset(UTF8_FULL)
92        .set_header(vec!["Active Repositories"]);
93    for repo in config.repos {
94        table.add_row(vec![repo]);
95    }
96    println!("{table}");
97    Ok(())
98}
99
100fn run_list_all() -> Result<()> {
101    let active_repos = config::read_config()?
102        .repos
103        .into_iter()
104        .collect::<HashSet<_>>();
105    let all_repos = config::get_all_repos()?;
106
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    let mut table = Table::new();
147    table
148        .load_preset(UTF8_FULL)
149        .set_header(vec!["Cloned Git Repositories (~/.zoi/pkgs/git)"]);
150    for repo in repos {
151        table.add_row(vec![repo]);
152    }
153    println!("{table}");
154    Ok(())
155}