bpi_rs/activity/
client.rs1use crate::activity::info::{ActivityInfoData, ActivityInfoParams};
2use crate::activity::list::{ActivityListData, ActivityListParams};
3use crate::{BilibiliRequest, BpiClient, BpiResult};
4
5const INFO_ENDPOINT: &str = "https://api.bilibili.com/x/activity/subject/info";
6const LIST_ENDPOINT: &str = "https://api.bilibili.com/x/activity/page/list";
7
8#[derive(Clone, Copy)]
10pub struct ActivityClient<'a> {
11 pub(crate) client: &'a BpiClient,
12}
13
14impl<'a> ActivityClient<'a> {
15 pub(crate) fn new(client: &'a BpiClient) -> Self {
16 Self { client }
17 }
18
19 #[cfg(test)]
20 pub(crate) fn info_endpoint(&self) -> &'static str {
21 INFO_ENDPOINT
22 }
23
24 #[cfg(test)]
25 pub(crate) fn list_endpoint(&self) -> &'static str {
26 LIST_ENDPOINT
27 }
28
29 pub async fn info(&self, params: ActivityInfoParams) -> BpiResult<ActivityInfoData> {
31 self.client
32 .get(INFO_ENDPOINT)
33 .query(¶ms.query_pairs())
34 .send_bpi_payload("activity.info")
35 .await
36 }
37
38 pub async fn list(&self, params: ActivityListParams) -> BpiResult<ActivityListData> {
40 self.client
41 .get(LIST_ENDPOINT)
42 .query(¶ms.query_pairs())
43 .send_bpi_payload("activity.list")
44 .await
45 }
46
47 pub async fn list_default(&self) -> BpiResult<ActivityListData> {
49 self.list(ActivityListParams::default()).await
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use std::future::Future;
56
57 use crate::activity::info::{ActivityInfoData, ActivityInfoParams};
58 use crate::activity::list::{ActivityListData, ActivityListParams};
59 use crate::ids::Bvid;
60 use crate::probe::contract::HttpMethod;
61 use crate::probe::endpoint_contract::EndpointContract;
62 use crate::{BpiClient, BpiResult};
63
64 fn assert_info_future<F>(_future: F)
65 where
66 F: Future<Output = BpiResult<ActivityInfoData>>,
67 {
68 }
69
70 fn assert_list_future<F>(_future: F)
71 where
72 F: Future<Output = BpiResult<ActivityListData>>,
73 {
74 }
75
76 fn contract(endpoint: &str) -> BpiResult<EndpointContract> {
77 let bytes = match endpoint {
78 "info" => {
79 include_bytes!("../../tests/contracts/activity/info/contract.json").as_slice()
80 }
81 "list" => {
82 include_bytes!("../../tests/contracts/activity/list/contract.json").as_slice()
83 }
84 _ => unreachable!("unknown activity endpoint fixture"),
85 };
86
87 EndpointContract::from_slice(bytes)
88 }
89
90 #[test]
91 fn activity_client_exposes_promoted_endpoint_urls() -> BpiResult<()> {
92 let client = BpiClient::new()?;
93 let activity = client.activity();
94
95 assert_eq!(
96 activity.info_endpoint(),
97 "https://api.bilibili.com/x/activity/subject/info"
98 );
99 assert_eq!(
100 activity.list_endpoint(),
101 "https://api.bilibili.com/x/activity/page/list"
102 );
103 Ok(())
104 }
105
106 #[test]
107 fn activity_methods_return_payload_futures() -> BpiResult<()> {
108 let client = BpiClient::new()?;
109 let activity = client.activity();
110 let bvid: Bvid = "BV1mKY4e8ELy".parse()?;
111
112 assert_info_future(activity.info(ActivityInfoParams::new(4_017_552)?.with_bvid(bvid)));
113 assert_list_future(activity.list(ActivityListParams::new().page_size(1)?));
114 assert_list_future(activity.list_default());
115 Ok(())
116 }
117
118 #[test]
119 fn activity_contracts_match_module_client_endpoints() -> BpiResult<()> {
120 let client = BpiClient::new()?;
121 let activity = client.activity();
122 let info = contract("info")?;
123 let list = contract("list")?;
124
125 assert_eq!(info.name, "activity.info");
126 assert_eq!(info.request.method, HttpMethod::Get);
127 assert_eq!(info.request.url.as_str(), activity.info_endpoint());
128 assert_eq!(
129 info.request.query.get("sid").map(String::as_str),
130 Some("4017552")
131 );
132 assert_eq!(
133 info.request.query.get("bvid").map(String::as_str),
134 Some("BV1mKY4e8ELy")
135 );
136
137 assert_eq!(list.name, "activity.list");
138 assert_eq!(list.request.method, HttpMethod::Get);
139 assert_eq!(list.request.url.as_str(), activity.list_endpoint());
140 assert_eq!(
141 list.request.query.get("plat").map(String::as_str),
142 Some("1,3")
143 );
144 assert_eq!(list.request.query.get("ps").map(String::as_str), Some("1"));
145 Ok(())
146 }
147}