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::for_test()
94 }
95
96 #[tokio::test]
97 async fn webinars_list_empty_is_ok() {
98 let server = MockServer::start().await;
99 Mock::given(method("GET"))
100 .and(path("/v2/users/me/webinars"))
101 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
102 "webinars": [],
103 "total_records": 0
104 })))
105 .mount(&server)
106 .await;
107
108 let mut client =
109 ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
110 list(&mut client, &test_out(), "me").await.unwrap();
111 }
112
113 #[tokio::test]
114 async fn webinars_list_returns_table_data() {
115 let server = MockServer::start().await;
116 Mock::given(method("GET"))
117 .and(path("/v2/users/me/webinars"))
118 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
119 "webinars": [
120 {
121 "id": 12345678,
122 "topic": "Annual Summit",
123 "start_time": "2026-06-01T09:00:00Z",
124 "duration": 120,
125 "type": 5
126 }
127 ],
128 "total_records": 1
129 })))
130 .mount(&server)
131 .await;
132
133 let mut client =
134 ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
135 list(&mut client, &test_out(), "me").await.unwrap();
136 }
137
138 #[tokio::test]
139 async fn webinars_get_returns_webinar() {
140 let server = MockServer::start().await;
141 Mock::given(method("GET"))
142 .and(path("/v2/webinars/12345678"))
143 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
144 "id": 12345678,
145 "topic": "Annual Summit",
146 "start_time": "2026-06-01T09:00:00Z",
147 "duration": 120,
148 "join_url": "https://zoom.us/j/12345678",
149 "status": "waiting",
150 "agenda": "Keynote and breakouts",
151 "type": 5
152 })))
153 .mount(&server)
154 .await;
155
156 let mut client =
157 ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
158 get(&mut client, &test_out(), 12345678).await.unwrap();
159 }
160
161 #[tokio::test]
162 async fn webinars_get_not_found_propagates() {
163 let server = MockServer::start().await;
164 Mock::given(method("GET"))
165 .and(path("/v2/webinars/99999999"))
166 .respond_with(ResponseTemplate::new(404).set_body_string("Webinar not found"))
167 .mount(&server)
168 .await;
169
170 let mut client =
171 ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
172 let err = get(&mut client, &test_out(), 99999999).await.unwrap_err();
173 assert!(matches!(err, ApiError::NotFound(_)));
174 }
175}