1use crate::{proto, token};
2use anyhow::{anyhow, Result};
3use prost::Message;
4use reqwest::header::CONTENT_TYPE;
5use std::future::Future;
6
7pub struct Client {
8 http: reqwest::Client,
9 uri: String,
10 key: String,
11 secret: String,
12}
13
14impl Client {
15 pub fn new(mut uri: String, key: String, secret: String) -> Self {
16 if uri.ends_with('/') {
17 uri.pop();
18 }
19
20 Self {
21 http: reqwest::Client::new(),
22 uri,
23 key,
24 secret,
25 }
26 }
27
28 pub fn create_room(&self, name: String) -> impl Future<Output = Result<proto::Room>> {
29 self.request(
30 "twirp/livekit.RoomService/CreateRoom",
31 token::VideoGrant {
32 room_create: Some(true),
33 ..Default::default()
34 },
35 proto::CreateRoomRequest {
36 name,
37 ..Default::default()
38 },
39 )
40 }
41
42 pub fn delete_room(&self, name: String) -> impl Future<Output = Result<()>> {
43 let response = self.request(
44 "twirp/livekit.RoomService/DeleteRoom",
45 token::VideoGrant {
46 room_create: Some(true),
47 ..Default::default()
48 },
49 proto::DeleteRoomRequest { room: name },
50 );
51 async move {
52 response.await?;
53 Ok(())
54 }
55 }
56
57 fn request<Req, Res>(
58 &self,
59 path: &str,
60 grant: token::VideoGrant,
61 body: Req,
62 ) -> impl Future<Output = Result<Res>>
63 where
64 Req: Message,
65 Res: Default + Message,
66 {
67 let client = self.http.clone();
68 let token = token::create(&self.key, &self.secret, None, grant);
69 let uri = format!("{}/{}", self.uri, path);
70 async move {
71 let token = token?;
72 let response = client
73 .post(&uri)
74 .header(CONTENT_TYPE, "application/protobuf")
75 .bearer_auth(token)
76 .body(body.encode_to_vec())
77 .send()
78 .await?;
79 if response.status().is_success() {
80 Ok(Res::decode(response.bytes().await?)?)
81 } else {
82 Err(anyhow!(
83 "POST {} failed with status code {:?}, {:?}",
84 uri,
85 response.status(),
86 response.text().await
87 ))
88 }
89 }
90 }
91}