main
rs 107 lines 3.13 KB
Raw
1 use {
2 crate::{
3 cli::RepoCommand,
4 context::Ctx,
5 ui::{self, prompt},
6 },
7 anyhow::Result,
8 sigit_si_api::NewRepository,
9 };
10
11 pub async fn run(ctx: &Ctx, command: RepoCommand) -> Result<()> {
12 ctx.require_token()?;
13
14 match command {
15 RepoCommand::List => list(ctx).await,
16 RepoCommand::Search { query, limit } => search(ctx, query, limit).await,
17 RepoCommand::View { repo } => view(ctx, repo).await,
18 RepoCommand::Create {
19 name,
20 private,
21 description,
22 yes,
23 } => create(ctx, name, private, description, yes).await,
24 RepoCommand::Browse { repo } => browse(ctx, repo).await,
25 }
26 }
27
28 async fn list(ctx: &Ctx) -> Result<()> {
29 let repos = ctx.client.repos().await?;
30 ui::emit(ctx.json, &repos, |repos| ui::render::repos(repos))
31 }
32
33 async fn search(ctx: &Ctx, query: Option<String>, limit: u32) -> Result<()> {
34 let repos = ctx
35 .client
36 .search_repos(query.as_deref(), Some(limit))
37 .await?;
38 ui::emit(ctx.json, &repos, |repos| ui::render::repo_summaries(repos))
39 }
40
41 async fn view(ctx: &Ctx, repo: Option<String>) -> Result<()> {
42 let slug = ctx.resolve_repo(repo)?;
43
44 // `GET /api/v1/repos` only lists the signed-in user's own repos, but
45 // `view` should also work on repos owned by someone else — so try the
46 // owned list first (it has the fuller `Repository` shape) and fall back
47 // to the cross-user MCP search.
48 if let Some(repo) = ctx
49 .client
50 .repos()
51 .await?
52 .into_iter()
53 .find(|r| r.full_name.eq_ignore_ascii_case(&slug.to_string()))
54 {
55 return ui::emit(ctx.json, &repo, ui::render::repo_detail);
56 }
57
58 let matches = ctx.client.search_repos(Some(&slug.name), None).await?;
59 let summary = matches
60 .into_iter()
61 .find(|r| r.full_name.eq_ignore_ascii_case(&slug.to_string()))
62 .ok_or_else(|| anyhow::anyhow!("Repository {slug} was not found."))?;
63 ui::emit(ctx.json, &summary, |summary| {
64 ui::render::repo_summaries(std::slice::from_ref(summary))
65 })
66 }
67
68 async fn create(
69 ctx: &Ctx,
70 name: String,
71 private: bool,
72 description: Option<String>,
73 yes: bool,
74 ) -> Result<()> {
75 if !yes {
76 let confirmed = prompt::confirm(
77 &format!(
78 "Create {} repository {}?",
79 if private { "private" } else { "public" },
80 ui::highlight(&name)
81 ),
82 true,
83 )?;
84 if !confirmed {
85 ui::note("Cancelled.");
86 return Ok(());
87 }
88 }
89
90 let new_repo = NewRepository::new(&name)
91 .private(private)
92 .description(description);
93 let repo = ctx.client.create_repo(&new_repo).await?;
94
95 ui::emit(ctx.json, &repo, |repo| {
96 ui::success(format!("Created {}.", ui::highlight(&repo.full_name)));
97 println!(" {}", repo.clone_url);
98 })
99 }
100
101 async fn browse(ctx: &Ctx, repo: Option<String>) -> Result<()> {
102 let slug = ctx.resolve_repo(repo)?;
103 let url = format!("{}/{slug}", ctx.client.base_url());
104 open::that(&url)?;
105 ui::note(format!("Opened {url}"));
106 Ok(())
107 }