use std::io::Write;
use anyhow::{anyhow, Result};
use clap::{Args, Subcommand};
use serde_json::{json, Value};
use crate::client;
use crate::output::{render_table, FormatChoice};
#[derive(Debug, Subcommand)]
pub enum OrgCmd {
List(FormatArgs),
Members(LoginArgs),
Teams(LoginArgs),
}
#[derive(Debug, Args)]
pub struct FormatArgs {
#[arg(long, default_value_t = false)]
pub json: bool,
#[arg(long, default_value_t = false)]
pub tsv: bool,
}
#[derive(Debug, Args)]
pub struct LoginArgs {
pub login: String,
#[arg(long, default_value_t = false)]
pub json: bool,
#[arg(long, default_value_t = false)]
pub tsv: bool,
}
pub fn run(cmd: OrgCmd) -> Result<()> {
match cmd {
OrgCmd::List(a) => list(a),
OrgCmd::Members(a) => members(a),
OrgCmd::Teams(a) => teams(a),
}
}
fn list(args: FormatArgs) -> Result<()> {
let (cli, _cfg) = client::authenticated()?;
let data = cli.execute(
"{ viewer { organizations { login displayName description } } }",
json!({}),
)?;
let orgs = data["viewer"]["organizations"]
.as_array()
.cloned()
.unwrap_or_default();
let rows = org_rows(&orgs);
let fmt = FormatChoice {
json: args.json,
tsv: args.tsv,
}
.resolve();
std::io::stdout()
.lock()
.write_all(render_table(&["login", "name", "description"], &rows, fmt).as_bytes())?;
Ok(())
}
fn members(args: LoginArgs) -> Result<()> {
let (cli, _cfg) = client::authenticated()?;
let data = cli.execute(
"query($login: String!) { organization(login: $login) { members { role account { username displayName } } } }",
json!({ "login": args.login }),
)?;
let org = &data["organization"];
if org.is_null() {
return Err(anyhow!("no organization `{}`", args.login));
}
let members = org["members"].as_array().cloned().unwrap_or_default();
let rows = member_rows(&members);
let fmt = FormatChoice {
json: args.json,
tsv: args.tsv,
}
.resolve();
std::io::stdout()
.lock()
.write_all(render_table(&["user", "name", "role"], &rows, fmt).as_bytes())?;
Ok(())
}
fn teams(args: LoginArgs) -> Result<()> {
let (cli, _cfg) = client::authenticated()?;
let data = cli.execute(
"query($login: String!) { organization(login: $login) { teams { slug name description } } }",
json!({ "login": args.login }),
)?;
let org = &data["organization"];
if org.is_null() {
return Err(anyhow!("no organization `{}`", args.login));
}
let teams = org["teams"].as_array().cloned().unwrap_or_default();
let rows = team_rows(&teams);
let fmt = FormatChoice {
json: args.json,
tsv: args.tsv,
}
.resolve();
std::io::stdout()
.lock()
.write_all(render_table(&["slug", "name", "description"], &rows, fmt).as_bytes())?;
Ok(())
}
fn org_rows(orgs: &[Value]) -> Vec<Vec<String>> {
orgs.iter()
.map(|o| {
vec![
o["login"].as_str().unwrap_or("").to_string(),
o["displayName"].as_str().unwrap_or("").to_string(),
o["description"].as_str().unwrap_or("").to_string(),
]
})
.collect()
}
fn member_rows(members: &[Value]) -> Vec<Vec<String>> {
members
.iter()
.map(|m| {
vec![
m["account"]["username"].as_str().unwrap_or("").to_string(),
m["account"]["displayName"]
.as_str()
.unwrap_or("")
.to_string(),
m["role"].as_str().unwrap_or("").to_string(),
]
})
.collect()
}
fn team_rows(teams: &[Value]) -> Vec<Vec<String>> {
teams
.iter()
.map(|t| {
vec![
t["slug"].as_str().unwrap_or("").to_string(),
t["name"].as_str().unwrap_or("").to_string(),
t["description"].as_str().unwrap_or("").to_string(),
]
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn org_rows_format_org_member_and_team_tables_with_defaults() {
let orgs = vec![
json!({
"login": "runtime-labs",
"displayName": "Runtime Labs",
"description": "Builds Runtime"
}),
json!({}),
];
assert_eq!(
org_rows(&orgs),
vec![
vec!["runtime-labs", "Runtime Labs", "Builds Runtime"],
vec!["", "", ""],
]
);
let members = vec![
json!({
"role": "ADMIN",
"account": { "username": "bri", "displayName": "Bri" }
}),
json!({}),
];
assert_eq!(
member_rows(&members),
vec![vec!["bri", "Bri", "ADMIN"], vec!["", "", ""]]
);
let teams = vec![
json!({ "slug": "ops", "name": "Ops", "description": "Operators" }),
json!({}),
];
assert_eq!(
team_rows(&teams),
vec![vec!["ops", "Ops", "Operators"], vec!["", "", ""]]
);
}
}