1#![allow(clippy::print_stdout)]
6
7use crate::cli::ClassroomCommand;
8use crate::client::Client;
9use crate::output::print_json;
10use anyhow::Context;
11
12pub fn run(cmd: ClassroomCommand) -> anyhow::Result<()> {
18 match cmd {
19 ClassroomCommand::List { json, hostname } => list(json, hostname.as_deref()),
20 ClassroomCommand::Assignments { id, json, hostname } => {
21 assignments(id, json, hostname.as_deref())
22 }
23 }
24}
25
26fn list(json: Option<Vec<String>>, hostname: Option<&str>) -> anyhow::Result<()> {
32 let host = hostname.unwrap_or("github.com");
33 let client = Client::new(host).context("failed to create HTTP client")?;
34
35 let response = client
36 .get("/classrooms")
37 .context("failed to fetch classrooms")?;
38
39 let status = response.status();
40 if !status.is_success() {
41 anyhow::bail!("failed to list classrooms: HTTP {status}");
42 }
43
44 let classrooms: Vec<serde_json::Value> = response.json().context("failed to parse response")?;
45
46 if let Some(fields) = json {
47 let fields_ref: Option<&[String]> = if fields.is_empty() {
48 None
49 } else {
50 Some(&fields)
51 };
52 print_json(&classrooms, fields_ref);
53 return Ok(());
54 }
55
56 if classrooms.is_empty() {
57 println!("No classrooms found.");
58 return Ok(());
59 }
60
61 println!("{:<8} {:<30} ORGANIZATION", "ID", "NAME");
62 for c in &classrooms {
63 let id = c["id"].as_u64().unwrap_or(0);
64 let name = c["name"].as_str().unwrap_or("—");
65 let org = c["organization"]["login"].as_str().unwrap_or("—");
66 let name_truncated = crate::cmd::util::truncate(name, 30);
67 println!("{id:<8} {name_truncated:<30} {org}");
68 }
69
70 Ok(())
71}
72
73fn assignments(id: u64, json: Option<Vec<String>>, hostname: Option<&str>) -> anyhow::Result<()> {
79 let host = hostname.unwrap_or("github.com");
80 let client = Client::new(host).context("failed to create HTTP client")?;
81
82 let path = format!("/classrooms/{id}/assignments");
83 let response = client.get(&path).context("failed to fetch assignments")?;
84
85 let status = response.status();
86 if !status.is_success() {
87 anyhow::bail!("failed to list assignments: HTTP {status}");
88 }
89
90 let assignments: Vec<serde_json::Value> =
91 response.json().context("failed to parse response")?;
92
93 if let Some(fields) = json {
94 let fields_ref: Option<&[String]> = if fields.is_empty() {
95 None
96 } else {
97 Some(&fields)
98 };
99 print_json(&assignments, fields_ref);
100 return Ok(());
101 }
102
103 if assignments.is_empty() {
104 println!("No assignments found for classroom {id}.");
105 return Ok(());
106 }
107
108 println!("{:<8} {:<30} {:<20} INVITED", "ID", "TITLE", "DEADLINE");
109 for a in &assignments {
110 let aid = a["id"].as_u64().unwrap_or(0);
111 let title = a["title"].as_str().unwrap_or("—");
112 let deadline = a["deadline"].as_str().unwrap_or("—");
113 let invited = a["invitations_count"].as_u64().unwrap_or(0);
114 let title_truncated = crate::cmd::util::truncate(title, 30);
115 println!("{aid:<8} {title_truncated:<30} {deadline:<20} {invited}");
116 }
117
118 Ok(())
119}