1use std::io::Write;
4
5use anyhow::{anyhow, Result};
6use clap::{Args, Subcommand};
7use serde_json::{json, Value};
8
9use crate::client;
10use crate::output::{render_table, FormatChoice};
11
12#[derive(Debug, Subcommand)]
13pub enum OrgCmd {
14 List(FormatArgs),
16 Members(LoginArgs),
18 Teams(LoginArgs),
20}
21
22#[derive(Debug, Args)]
23pub struct FormatArgs {
24 #[arg(long, default_value_t = false)]
25 pub json: bool,
26 #[arg(long, default_value_t = false)]
27 pub tsv: bool,
28}
29
30#[derive(Debug, Args)]
31pub struct LoginArgs {
32 pub login: String,
33 #[arg(long, default_value_t = false)]
34 pub json: bool,
35 #[arg(long, default_value_t = false)]
36 pub tsv: bool,
37}
38
39pub fn run(cmd: OrgCmd) -> Result<()> {
40 match cmd {
41 OrgCmd::List(a) => list(a),
42 OrgCmd::Members(a) => members(a),
43 OrgCmd::Teams(a) => teams(a),
44 }
45}
46
47fn list(args: FormatArgs) -> Result<()> {
48 let (cli, _cfg) = client::authenticated()?;
49 let data = cli.execute(
50 "{ viewer { organizations { login displayName description } } }",
51 json!({}),
52 )?;
53 let orgs = data["viewer"]["organizations"]
54 .as_array()
55 .cloned()
56 .unwrap_or_default();
57 let rows = org_rows(&orgs);
58 let fmt = FormatChoice {
59 json: args.json,
60 tsv: args.tsv,
61 }
62 .resolve();
63 std::io::stdout()
64 .lock()
65 .write_all(render_table(&["login", "name", "description"], &rows, fmt).as_bytes())?;
66 Ok(())
67}
68
69fn members(args: LoginArgs) -> Result<()> {
70 let (cli, _cfg) = client::authenticated()?;
71 let data = cli.execute(
72 "query($login: String!) { organization(login: $login) { members { role account { username displayName } } } }",
73 json!({ "login": args.login }),
74 )?;
75 let org = &data["organization"];
76 if org.is_null() {
77 return Err(anyhow!("no organization `{}`", args.login));
78 }
79 let members = org["members"].as_array().cloned().unwrap_or_default();
80 let rows = member_rows(&members);
81 let fmt = FormatChoice {
82 json: args.json,
83 tsv: args.tsv,
84 }
85 .resolve();
86 std::io::stdout()
87 .lock()
88 .write_all(render_table(&["user", "name", "role"], &rows, fmt).as_bytes())?;
89 Ok(())
90}
91
92fn teams(args: LoginArgs) -> Result<()> {
93 let (cli, _cfg) = client::authenticated()?;
94 let data = cli.execute(
95 "query($login: String!) { organization(login: $login) { teams { slug name description } } }",
96 json!({ "login": args.login }),
97 )?;
98 let org = &data["organization"];
99 if org.is_null() {
100 return Err(anyhow!("no organization `{}`", args.login));
101 }
102 let teams = org["teams"].as_array().cloned().unwrap_or_default();
103 let rows = team_rows(&teams);
104 let fmt = FormatChoice {
105 json: args.json,
106 tsv: args.tsv,
107 }
108 .resolve();
109 std::io::stdout()
110 .lock()
111 .write_all(render_table(&["slug", "name", "description"], &rows, fmt).as_bytes())?;
112 Ok(())
113}
114
115fn org_rows(orgs: &[Value]) -> Vec<Vec<String>> {
116 orgs.iter()
117 .map(|o| {
118 vec![
119 o["login"].as_str().unwrap_or("").to_string(),
120 o["displayName"].as_str().unwrap_or("").to_string(),
121 o["description"].as_str().unwrap_or("").to_string(),
122 ]
123 })
124 .collect()
125}
126
127fn member_rows(members: &[Value]) -> Vec<Vec<String>> {
128 members
129 .iter()
130 .map(|m| {
131 vec![
132 m["account"]["username"].as_str().unwrap_or("").to_string(),
133 m["account"]["displayName"]
134 .as_str()
135 .unwrap_or("")
136 .to_string(),
137 m["role"].as_str().unwrap_or("").to_string(),
138 ]
139 })
140 .collect()
141}
142
143fn team_rows(teams: &[Value]) -> Vec<Vec<String>> {
144 teams
145 .iter()
146 .map(|t| {
147 vec![
148 t["slug"].as_str().unwrap_or("").to_string(),
149 t["name"].as_str().unwrap_or("").to_string(),
150 t["description"].as_str().unwrap_or("").to_string(),
151 ]
152 })
153 .collect()
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn org_rows_format_org_member_and_team_tables_with_defaults() {
162 let orgs = vec![
163 json!({
164 "login": "runtime-labs",
165 "displayName": "Runtime Labs",
166 "description": "Builds Runtime"
167 }),
168 json!({}),
169 ];
170 assert_eq!(
171 org_rows(&orgs),
172 vec![
173 vec!["runtime-labs", "Runtime Labs", "Builds Runtime"],
174 vec!["", "", ""],
175 ]
176 );
177
178 let members = vec![
179 json!({
180 "role": "ADMIN",
181 "account": { "username": "bri", "displayName": "Bri" }
182 }),
183 json!({}),
184 ];
185 assert_eq!(
186 member_rows(&members),
187 vec![vec!["bri", "Bri", "ADMIN"], vec!["", "", ""]]
188 );
189
190 let teams = vec![
191 json!({ "slug": "ops", "name": "Ops", "description": "Operators" }),
192 json!({}),
193 ];
194 assert_eq!(
195 team_rows(&teams),
196 vec![vec!["ops", "Ops", "Operators"], vec!["", "", ""]]
197 );
198 }
199}