1#![allow(clippy::print_stdout)]
6
7use crate::cli::OrgCommand;
8use crate::client::Client;
9use crate::output::print_json;
10use anyhow::Context;
11
12pub fn run(cmd: OrgCommand) -> anyhow::Result<()> {
18 match cmd {
19 OrgCommand::List {
20 limit,
21 json,
22 hostname,
23 } => list(limit, json, hostname.as_deref()),
24 OrgCommand::View {
25 org,
26 web,
27 json,
28 hostname,
29 } => view(&org, web, json, hostname.as_deref()),
30 }
31}
32
33fn list(limit: u32, json: Option<Vec<String>>, hostname: Option<&str>) -> anyhow::Result<()> {
41 let host = hostname.unwrap_or("github.com");
42 let client = Client::new(host).context("failed to create HTTP client")?;
43
44 let path = format!("/user/orgs?per_page={}", limit.min(100));
45 let response = client.get(&path).context("failed to fetch organizations")?;
46
47 let status = response.status();
48 if !status.is_success() {
49 anyhow::bail!("failed to list organizations: HTTP {status}");
50 }
51
52 let mut orgs: Vec<serde_json::Value> =
53 response.json().context("failed to parse orgs response")?;
54
55 orgs.truncate(limit as usize);
56
57 if let Some(fields) = json {
58 let fields_ref: Option<&[String]> = if fields.is_empty() {
59 None
60 } else {
61 Some(&fields)
62 };
63 print_json(&orgs, fields_ref);
64 return Ok(());
65 }
66
67 print_org_table(&orgs);
68 Ok(())
69}
70
71fn view(
79 org: &str,
80 web: bool,
81 json: Option<Vec<String>>,
82 hostname: Option<&str>,
83) -> anyhow::Result<()> {
84 let host = hostname.unwrap_or("github.com");
85 let client = Client::new(host).context("failed to create HTTP client")?;
86
87 let path = format!("/orgs/{org}");
88 let response = client.get(&path).context("failed to fetch organization")?;
89
90 let status = response.status();
91 if status == reqwest::StatusCode::NOT_FOUND {
92 anyhow::bail!("organization '{org}' not found");
93 }
94 if !status.is_success() {
95 anyhow::bail!("failed to view organization: HTTP {status}");
96 }
97
98 let org_data: serde_json::Value = response.json().context("failed to parse response")?;
99
100 if web {
102 if let Some(url) = org_data["html_url"].as_str() {
103 crate::cmd::browse::open_in_browser(url);
104 return Ok(());
105 }
106 }
107
108 if let Some(fields) = json {
110 let fields_ref: Option<&[String]> = if fields.is_empty() {
111 None
112 } else {
113 Some(&fields)
114 };
115 print_json(&org_data, fields_ref);
116 return Ok(());
117 }
118
119 let name = org_data["name"].as_str().unwrap_or("—");
121 let description = org_data["description"].as_str().unwrap_or("No description");
122 let location = org_data["location"].as_str().unwrap_or("—");
123 let blog = org_data["blog"].as_str().unwrap_or("—");
124 let email = org_data["email"].as_str().unwrap_or("—");
125 let members = org_data["members_count"].as_u64().unwrap_or(0);
126 let repos = org_data["public_repos"].as_u64().unwrap_or(0);
127
128 println!(" Name: {name}");
129 println!(" Description: {description}");
130 println!(" Location: {location}");
131 println!(" Website: {blog}");
132 println!(" Email: {email}");
133 println!(" Members: {members}");
134 println!(" Public repos: {repos}");
135
136 Ok(())
137}
138
139fn print_org_table(orgs: &[serde_json::Value]) {
141 if orgs.is_empty() {
142 println!("No organizations found.");
143 return;
144 }
145
146 let login_width = 20;
147 let desc_width = 50;
148
149 println!("{:<login_width$} {:<desc_width$}", "LOGIN", "DESCRIPTION");
150
151 for org in orgs {
152 let login = org["login"].as_str().unwrap_or("—");
153 let description = org["description"].as_str().unwrap_or("—");
154
155 let desc_truncated = crate::cmd::util::truncate(description, desc_width);
156
157 println!("{login:<login_width$} {desc_truncated:<desc_width$}");
158 }
159}
160
161#[cfg(test)]
162#[allow(clippy::expect_used)]
163mod tests {
164 use super::*;
165 use serde_json::json;
166
167 #[test]
168 fn print_org_table_basic() {
169 let orgs = vec![json!({
170 "login": "my-org",
171 "description": "My organization"
172 })];
173 print_org_table(&orgs);
174 }
175
176 #[test]
177 fn print_org_table_empty() {
178 let orgs: Vec<serde_json::Value> = vec![];
179 print_org_table(&orgs);
180 }
181}