1use std::collections::HashSet;
2
3use anyhow::{Result, anyhow};
4use clap::{Parser, Subcommand};
5use colored::Colorize;
6use comfy_table::Table;
7use comfy_table::presets::UTF8_FULL;
8
9use crate::pkg::config;
10
11#[derive(Parser)]
13pub struct RepoCommand {
14 #[arg(
16 short = 'y',
17 long,
18 help = "Automatically answer yes to all prompts",
19 global = true
20 )]
21 yes: bool,
22 #[command(subcommand)]
24 command: Commands
25}
26
27#[derive(Subcommand)]
29enum Commands {
30 #[command(alias = "a")]
32 Add {
33 repo_or_url: Option<String>
35 },
36 #[command(alias = "rm")]
38 Remove {
39 repo_name: String
41 },
42 #[command(alias = "ls")]
44 List {
45 #[command(subcommand)]
47 which: Option<ListSub>
48 },
49 #[command(subcommand)]
51 Git(GitCommand)
52}
53
54pub fn run(args: RepoCommand) -> Result<()> {
67 let yes = args.yes;
68 match args.command {
69 Commands::Add { repo_or_url } => {
70 if let Some(val) = repo_or_url {
71 if val.starts_with("http://")
72 || val.starts_with("https://")
73 || std::path::Path::new(&val)
74 .extension()
75 .is_some_and(|ext| ext.eq_ignore_ascii_case("git"))
76 {
77 config::clone_git_repo(&val)?;
78 } else {
79 config::add_repo(&val)?;
80 println!(
81 "Repository '{}' added successfully.",
82 val.green()
83 );
84 }
85 } else if yes {
86 return Err(anyhow!(
87 "A repository name or URL is required when using --yes."
88 ));
89 } else {
90 config::interactive_add_repo()?;
91 }
92 }
93 Commands::Remove { repo_name } => {
94 config::remove_repo(&repo_name)?;
95 println!(
96 "Repository '{}' removed successfully.",
97 repo_name.green()
98 );
99 }
100 Commands::List { which } => match which {
101 None => run_list_active()?,
102 Some(ListSub::All) => run_list_all()?
103 },
104 Commands::Git(cmd) => match cmd {
105 GitCommand::List => run_list_git_only()?,
106 GitCommand::Rm { repo_name } => {
107 config::remove_git_repo(&repo_name)?;
108 }
109 }
110 }
111 Ok(())
112}
113
114fn run_list_active() -> Result<()> {
116 let config = config::read_config()?;
117 if config.repos.is_empty() {
118 println!("No active repositories.");
119 return Ok(());
120 }
121
122 println!("{} Active repositories:", "::".bold().blue());
123 let mut table = Table::new();
124 table.load_style(UTF8_FULL).set_header(vec!["Repository"]);
125 for repo in config.repos {
126 table.add_row(vec![repo]);
127 }
128 println!("{table}");
129 Ok(())
130}
131
132fn run_list_all() -> Result<()> {
134 let active_repos = config::read_config()?
135 .repos
136 .into_iter()
137 .collect::<HashSet<_>>();
138 let all_repos = config::get_all_repos()?;
139
140 println!("{} All available repositories:", "::".bold().blue());
141 let mut table = Table::new();
142 table
143 .load_style(UTF8_FULL)
144 .set_header(vec!["Status", "Repository"]);
145
146 for repo in all_repos {
147 let status = if active_repos.contains(&repo.to_lowercase()) {
148 "Added"
149 } else {
150 ""
151 };
152 table.add_row(vec![status.to_string(), repo]);
153 }
154 println!("{table}");
155 Ok(())
156}
157
158#[derive(Subcommand)]
160enum ListSub {
161 All
163}
164
165#[derive(Subcommand)]
167enum GitCommand {
168 #[command(alias = "ls")]
170 List,
171 Rm {
173 repo_name: String
175 }
176}
177
178fn run_list_git_only() -> Result<()> {
180 let repos = config::list_git_repos()?;
181 if repos.is_empty() {
182 println!("No cloned git repositories.");
183 return Ok(());
184 }
185
186 println!(
187 "{} Cloned git repositories (~/.zoi/pkgs/git):",
188 "::".bold().blue()
189 );
190 let mut table = Table::new();
191 table.load_style(UTF8_FULL).set_header(vec!["Repository"]);
192 for repo in repos {
193 table.add_row(vec![repo]);
194 }
195 println!("{table}");
196 Ok(())
197}