gitee_cli_rs/cmd/
ssh_key.rs1use std::io::Write;
2
3use super::{confirm, Ctx};
4use crate::cli::SshKeyCmd;
5use crate::error::{GiteeError, Result};
6use crate::out;
7
8pub fn execute(ctx: &Ctx, cmd: SshKeyCmd) -> Result<()> {
9 match cmd {
10 SshKeyCmd::List { limit } => {
11 let items = ctx.client.users().keys(limit.limit)?;
12 let mut out = std::io::stdout().lock();
13 ctx.out
14 .render(&mut out, &items, |w| out::ssh_key_table(w, &items))?;
15 }
16 SshKeyCmd::Add { pubkey_file, title } => {
17 let contents = std::fs::read_to_string(&pubkey_file)
18 .map_err(|e| GiteeError::Usage(format!("read {pubkey_file}: {e}")))?;
19 let key = contents.trim();
20 if key.is_empty() {
21 return Err(GiteeError::Usage("public key file is empty".into()));
22 }
23 let title = title.unwrap_or_else(|| default_key_title(key));
24 let created = ctx.client.users().add_key(key, &title)?;
25 let items = [created];
26 let mut out = std::io::stdout().lock();
27 ctx.out
28 .render(&mut out, &items, |w| out::ssh_key_table(w, &items))?;
29 }
30 SshKeyCmd::Delete { id, yes } => {
31 confirm(&format!("Delete SSH key {id}"), yes)?;
32 ctx.client.users().delete_key(id)?;
33 writeln!(std::io::stdout().lock(), "Deleted SSH key {id}")?;
34 }
35 }
36 Ok(())
37}
38
39fn default_key_title(key: &str) -> String {
40 let mut parts = key.split_whitespace();
42 let _ty = parts.next();
43 let _blob = parts.next();
44 let comment: Vec<&str> = parts.collect();
45 if !comment.is_empty() {
46 return comment.join(" ");
47 }
48 let host = hostname();
49 let date = today();
50 format!("{host}-{date}")
51}
52
53fn hostname() -> String {
54 std::process::Command::new("hostname")
55 .output()
56 .ok()
57 .and_then(|o| String::from_utf8(o.stdout).ok())
58 .map(|s| s.trim().to_string())
59 .filter(|s| !s.is_empty())
60 .unwrap_or_else(|| "host".into())
61}
62
63fn today() -> String {
64 std::process::Command::new("date")
66 .arg("+%Y-%m-%d")
67 .output()
68 .ok()
69 .and_then(|o| String::from_utf8(o.stdout).ok())
70 .map(|s| s.trim().to_string())
71 .filter(|s| !s.is_empty())
72 .unwrap_or_else(|| "unknown-date".into())
73}
74
75#[cfg(test)]
76mod title_tests {
77 use super::default_key_title;
78
79 #[test]
80 fn uses_pubkey_comment_when_present() {
81 let title = default_key_title("ssh-ed25519 AAAAAcel comment@box");
82 assert_eq!(title, "comment@box");
83 let title = default_key_title("ssh-ed25519 AAAAAcel my laptop key");
84 assert_eq!(title, "my laptop key");
85 }
86}