1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
use clap::{Args, Subcommand};
use eyre::OptionExt;
use forgejo_api::Forgejo;
use crate::{repo::RepoInfo, SpecialRender};
#[derive(Args, Clone, Debug)]
pub struct UserCommand {
#[clap(long, short = 'R')]
remote: Option<String>,
#[clap(subcommand)]
command: UserSubcommand,
}
#[derive(Subcommand, Clone, Debug)]
pub enum UserSubcommand {
View {
/// The name of the user to view
user: Option<String>,
},
Browse {
/// The name of the user to open in your browser
user: Option<String>,
},
}
impl UserCommand {
pub async fn run(self, keys: &mut crate::KeyInfo, host_name: Option<&str>) -> eyre::Result<()> {
let repo = RepoInfo::get_current(host_name, None, self.remote.as_deref())?;
let api = keys.get_api(repo.host_url()).await?;
match self.command {
UserSubcommand::View { user } => view_user(&api, user.as_deref()).await?,
UserSubcommand::Browse { user } => {
browse_user(&api, repo.host_url(), user.as_deref()).await?
}
}
Ok(())
}
}
async fn view_user(api: &Forgejo, user: Option<&str>) -> eyre::Result<()> {
let SpecialRender {
bold,
dash,
bright_cyan,
light_grey,
reset,
..
} = *crate::special_render();
let user_data = match user {
Some(user) => api.user_get(user).await?,
None => api.user_get_current().await?,
};
let username = user_data
.login
.as_deref()
.ok_or_eyre("user has no username")?;
print!("{bright_cyan}{bold}{username}{reset}");
if let Some(pronouns) = user_data.pronouns.as_deref() {
if !pronouns.is_empty() {
print!("{light_grey} {dash} {bold}{pronouns}{reset}");
}
}
println!();
let followers = user_data.followers_count.unwrap_or_default();
let following = user_data.following_count.unwrap_or_default();
println!("{bold}{followers}{reset} followers {dash} {bold}{following}{reset} following");
let mut first = true;
if let Some(website) = user_data.website.as_deref() {
if !website.is_empty() {
print!("{bold}{website}{reset}");
first = false;
}
}
if let Some(email) = user_data.email.as_deref() {
if !email.is_empty() && !email.contains("noreply") {
if !first {
print!(" {dash} ");
}
print!("{bold}{email}{reset}");
}
}
if !first {
println!();
}
if let Some(desc) = user_data.description.as_deref() {
if !desc.is_empty() {
println!();
println!("{}", crate::markdown(desc));
println!();
}
}
let joined = user_data
.created
.ok_or_eyre("user does not have join date")?;
let date_format = time::macros::format_description!("[month repr:short] [day], [year]");
println!("Joined on {bold}{}{reset}", joined.format(&date_format)?);
Ok(())
}
async fn browse_user(api: &Forgejo, host_url: &url::Url, user: Option<&str>) -> eyre::Result<()> {
let username = match user {
Some(user) => user.to_owned(),
None => {
let myself = api.user_get_current().await?;
myself
.login
.ok_or_eyre("authenticated user does not have login")?
}
};
// `User` doesn't have an `html_url` field, so we gotta construct the user
// page url ourselves
let mut url = host_url.clone();
url.path_segments_mut()
.map_err(|_| eyre::eyre!("invalid host url"))?
.push(&username);
open::that(url.as_str())?;
Ok(())
}
|