zoom_cli/commands/
webinars.rs1use crate::api::{ApiError, ZoomClient};
2use crate::output::{self, OutputConfig};
3
4pub async fn list(client: &mut ZoomClient, out: &OutputConfig, user: &str) -> Result<(), ApiError> {
5 let result = client.list_webinars(user).await?;
6
7 if out.json {
8 out.print_data(&serde_json::to_string_pretty(&result).expect("serialize"));
9 } else {
10 if result.webinars.is_empty() {
11 out.print_message("No webinars found.");
12 return Ok(());
13 }
14 let rows: Vec<Vec<String>> = result
15 .webinars
16 .iter()
17 .map(|w| {
18 vec![
19 w.id.to_string(),
20 w.topic.clone(),
21 w.start_time
22 .as_deref()
23 .map(output::format_timestamp)
24 .unwrap_or_else(|| "-".into()),
25 w.duration
26 .map(|d| format!("{d} min"))
27 .unwrap_or_else(|| "-".into()),
28 ]
29 })
30 .collect();
31 out.print_data(&output::table(
32 &["ID", "TOPIC", "START TIME", "DURATION"],
33 &rows,
34 ));
35 if let Some(total) = result.total_records {
36 out.print_message(&format!("{total} webinar(s) total"));
37 }
38 }
39 Ok(())
40}
41
42pub async fn get(
43 client: &mut ZoomClient,
44 out: &OutputConfig,
45 webinar_id: u64,
46) -> Result<(), ApiError> {
47 let webinar = client.get_webinar(webinar_id).await?;
48
49 if out.json {
50 out.print_data(&serde_json::to_string_pretty(&webinar).expect("serialize"));
51 } else {
52 let join_url = webinar.join_url.clone().unwrap_or_else(|| "-".into());
53 out.print_data(&output::kv_block(&[
54 ("id", webinar.id.to_string()),
55 ("topic", webinar.topic.clone()),
56 (
57 "start_time",
58 webinar
59 .start_time
60 .as_deref()
61 .map(output::format_timestamp)
62 .unwrap_or_else(|| "-".into()),
63 ),
64 (
65 "duration",
66 webinar
67 .duration
68 .map(|d| format!("{d} min"))
69 .unwrap_or_else(|| "-".into()),
70 ),
71 (
72 "status",
73 webinar.status.clone().unwrap_or_else(|| "-".into()),
74 ),
75 ("join_url", output::hyperlink(&join_url)),
76 (
77 "agenda",
78 webinar.agenda.clone().unwrap_or_else(|| "-".into()),
79 ),
80 ]));
81 }
82 Ok(())
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use crate::api::ZoomClient;
89 use wiremock::matchers::{method, path};
90 use wiremock::{Mock, MockServer, ResponseTemplate};
91
92 fn test_out() -> OutputConfig {
93 OutputConfig {
94 json: true,
95 quiet: true,
96 }
97 }
98
99 #[tokio::test]
100 async fn webinars_list_empty_is_ok() {
101 let server = MockServer::start().await;
102 Mock::given(method("GET"))
103 .and(path("/v2/users/me/webinars"))
104 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
105 "webinars": [],
106 "total_records": 0
107 })))
108 .mount(&server)
109 .await;
110
111 let mut client =
112 ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
113 list(&mut client, &test_out(), "me").await.unwrap();
114 }
115
116 #[tokio::test]
117 async fn webinars_list_returns_table_data() {
118 let server = MockServer::start().await;
119 Mock::given(method("GET"))
120 .and(path("/v2/users/me/webinars"))
121 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
122 "webinars": [
123 {
124 "id": 12345678,
125 "topic": "Annual Summit",
126 "start_time": "2026-06-01T09:00:00Z",
127 "duration": 120,
128 "type": 5
129 }
130 ],
131 "total_records": 1
132 })))
133 .mount(&server)
134 .await;
135
136 let mut client =
137 ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
138 list(&mut client, &test_out(), "me").await.unwrap();
139 }
140
141 #[tokio::test]
142 async fn webinars_get_returns_webinar() {
143 let server = MockServer::start().await;
144 Mock::given(method("GET"))
145 .and(path("/v2/webinars/12345678"))
146 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
147 "id": 12345678,
148 "topic": "Annual Summit",
149 "start_time": "2026-06-01T09:00:00Z",
150 "duration": 120,
151 "join_url": "https://zoom.us/j/12345678",
152 "status": "waiting",
153 "agenda": "Keynote and breakouts",
154 "type": 5
155 })))
156 .mount(&server)
157 .await;
158
159 let mut client =
160 ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
161 get(&mut client, &test_out(), 12345678).await.unwrap();
162 }
163
164 #[tokio::test]
165 async fn webinars_get_not_found_propagates() {
166 let server = MockServer::start().await;
167 Mock::given(method("GET"))
168 .and(path("/v2/webinars/99999999"))
169 .respond_with(ResponseTemplate::new(404).set_body_string("Webinar not found"))
170 .mount(&server)
171 .await;
172
173 let mut client =
174 ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
175 let err = get(&mut client, &test_out(), 99999999).await.unwrap_err();
176 assert!(matches!(err, ApiError::NotFound(_)));
177 }
178}