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::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}