Skip to main content

zoom_cli/commands/
webinars.rs

1use 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}