use crate::client::{params, PixivAppClient};
use crate::models::{Illustration, Novel, PixivResult, PixivUserResult, PixivUserStateResult};
use crate::PixivAppError;
use reqwest_middleware::reqwest::{header::CONTENT_TYPE, Method};
use serde_json::Value;
use std::error::Error;
impl PixivAppClient {
pub async fn user(&self) -> Result<PixivUserStateResult, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/me/state", self.host);
let req = self.http_client.request(Method::GET, url);
self.process_request::<PixivUserStateResult>(req).await
}
pub async fn user_list(
&self,
illust_id: u32,
offset: Option<u32>,
) -> Result<Vec<Value>, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v2/user/list", self.host);
let req = self.http_client.request(Method::GET, url).query(&[
("user_id", illust_id.to_string()),
("filter", format!("for_{}", self.platform)),
("offset", offset.unwrap_or(0).to_string()),
]);
let res = self.process_request::<PixivResult>(req).await?;
res.users.ok_or(res.error.map_or_else(
|| Box::<dyn Error + Send + Sync>::from(PixivAppError::RequestFailed),
|v| Box::new(v),
))
}
pub async fn user_detail(
&self,
user_id: u32,
) -> Result<PixivUserResult, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/detail", self.host);
let req = self.http_client.request(Method::GET, url).query(&[
("user_id", user_id.to_string()),
("filter", format!("for_{}", self.platform)),
]);
self.process_request::<PixivUserResult>(req).await
}
pub async fn user_followers(
&self,
user_id: u32,
offset: Option<u32>,
) -> Result<Vec<PixivUserResult>, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/follower", self.host);
let req = self.http_client.request(Method::GET, url).query(&[
("user_id", user_id.to_string()),
("filter", format!("for_{}", self.platform)),
("offset", offset.unwrap_or(0).to_string()),
]);
let res = self.process_request::<PixivResult>(req).await?;
res.user_previews.ok_or(res.error.map_or_else(
|| Box::<dyn Error + Send + Sync>::from(PixivAppError::RequestFailed),
|v| Box::new(v),
))
}
pub async fn user_following(
&self,
user_id: u32,
offset: Option<u32>,
) -> Result<Vec<PixivUserResult>, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/following", self.host);
let req = self.http_client.request(Method::GET, url).query(&[
("user_id", user_id.to_string()),
("filter", format!("for_{}", self.platform)),
("offset", offset.unwrap_or(0).to_string()),
]);
let res = self.process_request::<PixivResult>(req).await?;
res.user_previews.ok_or(res.error.map_or_else(
|| Box::<dyn Error + Send + Sync>::from(PixivAppError::RequestFailed),
|v| Box::new(v),
))
}
pub async fn user_novels(
&self,
user_id: u32,
offset: Option<u32>,
) -> Result<Vec<Novel>, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/novels", self.host);
let req = self.http_client.request(Method::GET, url).query(&[
("user_id", user_id.to_string()),
("filter", format!("for_{}", self.platform)),
("offset", offset.unwrap_or(0).to_string()),
]);
let res = self.process_request::<PixivResult>(req).await?;
res.novels.ok_or(res.error.map_or_else(
|| Box::<dyn Error + Send + Sync>::from(PixivAppError::RequestFailed),
|v| Box::new(v),
))
}
pub async fn user_illusts(
&self,
user_id: u32,
r#type: &str,
offset: Option<u32>,
) -> Result<Vec<Illustration>, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/illusts", self.host);
let req = self.http_client.request(Method::GET, url).query(&[
("user_id", user_id.to_string()),
("type", r#type.to_string()),
("filter", format!("for_{}", self.platform)),
("offset", offset.unwrap_or(0).to_string()),
]);
let res = self.process_request::<PixivResult>(req).await?;
res.illusts.ok_or(res.error.map_or_else(
|| Box::<dyn Error + Send + Sync>::from(PixivAppError::RequestFailed),
|v| Box::new(v),
))
}
pub async fn user_mypixiv(
&self,
user_id: u32,
offset: Option<u32>,
) -> Result<Value, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/mypixiv", self.host);
let req = self.http_client.request(Method::GET, url).query(&[
("user_id", user_id.to_string()),
("offset", offset.unwrap_or(0).to_string()),
]);
self.process_request::<Value>(req).await
}
pub async fn user_related(
&self,
seed_user_id: u32,
offset: Option<u32>,
) -> Result<Vec<PixivUserResult>, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/related", self.host);
let req = self.http_client.request(Method::GET, url).query(&[
("filter", format!("for_{}", self.platform)),
("offset", offset.unwrap_or(0).to_string()),
("seed_user_id", seed_user_id.to_string()),
]);
let res = self.process_request::<PixivResult>(req).await?;
res.user_previews.ok_or(res.error.map_or_else(
|| Box::<dyn Error + Send + Sync>::from(PixivAppError::RequestFailed),
|v| Box::new(v),
))
}
pub async fn user_follow_add(
&self,
user_id: u32,
restrict: params::Visibility,
) -> Result<(), Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/follow/add", self.host);
let body = [
("user_id", user_id.to_string()),
("restrict", restrict.to_string()),
]
.iter()
.map(|e| format!("{}={}", e.0, e.1))
.collect::<Vec<_>>()
.join("&");
let req = self
.http_client
.request(Method::POST, url)
.header(CONTENT_TYPE, "application/x-www-form-urlencoded")
.body(body);
self.process_request::<PixivResult>(req).await?;
Ok(())
}
pub async fn user_follow_delete(
&self,
user_id: u32,
) -> Result<(), Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/follow/delete", self.host);
let req = self
.http_client
.request(Method::POST, url)
.header(CONTENT_TYPE, "application/x-www-form-urlencoded")
.body(format!("user_id={}", user_id));
self.process_request::<PixivResult>(req).await?;
Ok(())
}
pub async fn user_bookmarks(
&self,
user_id: u32,
restrict: params::Visibility,
offset: Option<u32>,
max_bookmark: Option<u32>,
tag: Option<String>,
) -> Result<Vec<Illustration>, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/bookmarks/illust", self.host);
let mut params = vec![
("user_id", user_id.to_string()),
("restrict", restrict.to_string()),
("filter", format!("for_{}", self.platform)),
("offset", offset.unwrap_or(0).to_string()),
];
if let Some(max_bookmark) = max_bookmark {
params.push(("max_bookmark_id", max_bookmark.to_string()));
}
if let Some(tag) = tag {
params.push(("tag", tag));
}
let req = self.http_client.request(Method::GET, url).query(¶ms);
let res = self.process_request::<PixivResult>(req).await?;
res.illusts.ok_or(res.error.map_or_else(
|| Box::<dyn Error + Send + Sync>::from(PixivAppError::RequestFailed),
|v| Box::new(v),
))
}
pub async fn user_bookmark_tags(
&self,
restrict: params::Visibility,
offset: Option<u32>,
) -> Result<PixivResult, Box<dyn Error + Send + Sync>> {
let url = format!("{}/v1/user/bookmark-tags/illust", self.host);
let req = self.http_client.request(Method::GET, url).query(&[
("restrict", restrict.to_string()),
("offset", offset.unwrap_or(0).to_string()),
]);
self.process_request::<PixivResult>(req).await
}
}
#[cfg(test)]
mod tests {
use crate::client::{params, tests::*};
use serial_test::serial;
use tokio::test;
#[test]
async fn test_user() {
let result = get_client().user().await;
assert!(
result.is_ok(),
"Failed to fetch user list: {}",
result.err().unwrap()
);
}
#[test]
async fn test_user_listing() {
let result = get_client().user_list(USER_ID, None).await;
assert!(
result.is_ok(),
"Failed to fetch user list: {}",
result.err().unwrap()
);
}
#[test]
async fn test_user_details() {
let result = get_client().user_detail(USER_ID).await;
assert!(
result.is_ok(),
"Failed to fetch user details: {}",
result.err().unwrap()
);
assert_eq!(u32::try_from(result.unwrap().user.id).unwrap(), USER_ID);
}
#[test]
async fn test_user_followers() {
let result = get_client().user_followers(USER_ID, None).await;
assert!(
result.is_ok(),
"Failed to fetch user followers: {}",
result.err().unwrap()
);
}
#[test]
async fn test_user_following() {
let result = get_client().user_following(USER_ID, None).await;
assert!(
result.is_ok(),
"Failed to fetch following users for user: {}",
result.err().unwrap()
);
}
#[test]
async fn test_user_novels() {
let result = get_client().user_novels(USER_ID, None).await;
assert!(
result.is_ok(),
"Failed to fetch novels for user: {}",
result.err().unwrap()
);
}
#[test]
async fn test_user_illusts() {
let result = get_client().user_illusts(USER_ID, "illust", None).await;
assert!(
result.is_ok(),
"Failed to fetch user illustrations: {}",
result.err().unwrap()
);
}
#[test]
async fn test_related_users() {
let result = get_client().user_related(USER_ID, None).await;
assert!(
result.is_ok(),
"Failed to fetch related users: {}",
result.err().unwrap()
);
}
#[test]
#[serial]
async fn test_following() {
let result = get_client()
.user_follow_add(USER_ID, params::Visibility::Public)
.await;
assert!(
result.is_ok(),
"Failed to follow user: {}",
result.err().unwrap()
);
}
#[test]
#[serial]
async fn test_unfollowing() {
let result = get_client().user_follow_delete(USER_ID).await;
assert!(
result.is_ok(),
"Failed to unfollow user: {}",
result.err().unwrap()
);
}
#[test]
async fn test_user_bookmarks() {
let result = get_client()
.user_bookmarks(USER_ID, params::Visibility::Public, None, None, None)
.await;
assert!(
result.is_ok(),
"Failed to follow user: {}",
result.err().unwrap()
);
}
}