use crate::BilibiliRequest;
use crate::bangumi::BangumiClient;
use crate::ids::SeasonId;
use crate::response::BpiResult;
use serde::{Deserialize, Serialize};
const FOLLOW_ENDPOINT: &str = "https://api.bilibili.com/pgc/web/follow/add";
const UNFOLLOW_ENDPOINT: &str = "https://api.bilibili.com/pgc/web/follow/del";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BangumiFollowResult {
pub fmid: i64,
pub relation: bool,
pub status: i32,
pub toast: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BangumiFollowParams {
season_id: SeasonId,
}
impl BangumiFollowParams {
pub fn new(season_id: SeasonId) -> Self {
Self { season_id }
}
pub(crate) fn form_pairs(&self, csrf: &str) -> Vec<(&'static str, String)> {
vec![
("season_id", self.season_id.to_string()),
("csrf", csrf.to_string()),
]
}
}
impl<'a> BangumiClient<'a> {
pub async fn follow(&self, params: BangumiFollowParams) -> BpiResult<BangumiFollowResult> {
let csrf = self.client.csrf()?;
self.client
.post(FOLLOW_ENDPOINT)
.with_bilibili_headers()
.form(¶ms.form_pairs(&csrf))
.send_bpi_payload("bangumi.follow")
.await
}
pub async fn unfollow(&self, params: BangumiFollowParams) -> BpiResult<BangumiFollowResult> {
let csrf = self.client.csrf()?;
self.client
.post(UNFOLLOW_ENDPOINT)
.with_bilibili_headers()
.form(¶ms.form_pairs(&csrf))
.send_bpi_payload("bangumi.unfollow")
.await
}
}
#[cfg(test)]
mod tests {
use crate::BpiError;
use crate::bangumi::BangumiFollowParams;
use crate::ids::SeasonId;
#[test]
fn bangumi_follow_params_serializes_season_id() -> Result<(), BpiError> {
let params = BangumiFollowParams::new(SeasonId::new(1172)?);
assert_eq!(
params.form_pairs("csrf-token"),
vec![
("season_id", "1172".to_string()),
("csrf", "csrf-token".to_string()),
]
);
Ok(())
}
#[test]
fn season_id_rejects_zero_before_follow_params() {
let err = SeasonId::new(0).unwrap_err();
assert!(matches!(
err,
BpiError::InvalidParameter {
field: "season_id",
..
}
));
}
}