extern crate chrono;
pub extern crate reqwest;
pub extern crate http;
extern crate serde;
extern crate serde_json;
#[cfg(test)]
extern crate kankyo;
#[macro_use]
extern crate log;
use std::borrow::{Borrow, Cow};
use std::collections::HashMap;
use std::error::Error;
use std::fmt::{self, Write};
use chrono::naive::NaiveDate;
use reqwest::Client;
use reqwest::Response;
use http::status::StatusCode;
pub use http::{header, HeaderMap, Method};
use reqwest::Url;
use serde_json::Value;
const CLIENT_ID: &str = "MOBrBDS8blbauoSck0ZfDbtuzpyT";
const CLIENT_SECRET: &str = "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj";
#[derive(Debug, Clone)]
pub struct Pixiv {
client: Client,
access_token: String,
refresh_token: String,
}
#[derive(Debug, Clone)]
pub struct PixivRequest {
method: Method,
url: Url,
headers: HeaderMap,
}
#[derive(Debug, Clone)]
pub struct PixivRequestBuilder<'a> {
pixiv: &'a Pixiv,
request: PixivRequest,
params: HashMap<&'a str, Cow<'a, str>>,
}
#[derive(Debug)]
pub struct AuthError {
reason: String,
}
impl Error for AuthError {
fn description(&self) -> &str {
"An error occurred while trying to authenticate."
}
}
impl fmt::Display for AuthError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"An error occurred while trying to authenticate. Reason: {:?}",
self.reason
)
}
}
#[derive(Debug, Clone, Copy)]
pub enum Publicity {
Public,
Private,
}
impl Publicity {
fn as_str(&self) -> &'static str {
match *self {
Publicity::Public => "public",
Publicity::Private => "private",
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum RankingType {
All,
Illust,
Manga,
Ugoira,
}
impl RankingType {
fn as_str(&self) -> &'static str {
match *self {
RankingType::All => "all",
RankingType::Illust => "illust",
RankingType::Manga => "manga",
RankingType::Ugoira => "ugoira",
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum RankingMode {
Daily,
Weekly,
Monthly,
Rookie,
Original,
Male,
Female,
DailyR18,
WeeklyR18,
MaleR18,
FemaleR18,
R18G,
}
impl RankingMode {
fn as_str(&self) -> &'static str {
match *self {
RankingMode::Daily => "daily",
RankingMode::Weekly => "weekly",
RankingMode::Monthly => "monthly",
RankingMode::Rookie => "rookie",
RankingMode::Original => "original",
RankingMode::Male => "male",
RankingMode::Female => "female",
RankingMode::DailyR18 => "daily_r18",
RankingMode::WeeklyR18 => "weekly_r18",
RankingMode::MaleR18 => "male_r18",
RankingMode::FemaleR18 => "female_r18",
RankingMode::R18G => "r18g",
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum SearchPeriod {
All,
Day,
Week,
Month,
}
impl SearchPeriod {
fn as_str(&self) -> &'static str {
match *self {
SearchPeriod::All => "all",
SearchPeriod::Day => "day",
SearchPeriod::Week => "week",
SearchPeriod::Month => "month",
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum SearchMode {
Text,
Tag,
ExactTag,
Caption,
}
impl SearchMode {
fn as_str(&self) -> &'static str {
match *self {
SearchMode::Text => "text",
SearchMode::Tag => "tag",
SearchMode::ExactTag => "exact_tag",
SearchMode::Caption => "caption",
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum SearchOrder {
Descending,
Ascending,
}
impl SearchOrder {
fn as_str(&self) -> &'static str {
match *self {
SearchOrder::Descending => "desc",
SearchOrder::Ascending => "asc",
}
}
}
impl Pixiv {
#[inline]
pub fn new(client: &Client) -> Pixiv {
Pixiv {
client: client.clone(),
access_token: String::default(),
refresh_token: String::default(),
}
}
pub fn login(&mut self, username: &str, password: &str) -> Result<(), AuthError> {
let mut data = HashMap::new();
data.insert("client_id", CLIENT_ID);
data.insert("client_secret", CLIENT_SECRET);
data.insert("get_secure_url", "1");
data.insert("grant_type", "password");
data.insert("username", username);
data.insert("password", password);
let mut res = self.send_auth_request(&data)
.expect("Error occured while requesting token.");
match res.status() {
StatusCode::OK | StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND => {
}
s => {
return Err(AuthError {
reason: format!(
"Login failed. Check your username and password. Response: {:?}",
s
),
})
}
}
let mut json_response: Value = res.json().unwrap();
self.access_token = match json_response["response"]["access_token"].take() {
Value::String(s) => s,
_ => panic!("Failed to get access token."),
};
self.refresh_token = match json_response["response"]["refresh_token"].take() {
Value::String(s) => s,
_ => panic!("Failed to get refresh token."),
};
Ok(())
}
pub fn refresh_auth(&mut self) -> Result<(), AuthError> {
let refresh_clone = self.refresh_token.clone();
let mut data = HashMap::new();
data.insert("client_id", CLIENT_ID);
data.insert("client_secret", CLIENT_SECRET);
data.insert("get_secure_url", "1");
data.insert("grant_type", "refresh_token");
data.insert("refresh_token", refresh_clone.as_str());
let mut res = self.send_auth_request(&data)
.expect("Error occured while requesting token.");
match res.status() {
StatusCode::OK | StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND => {
}
s => {
return Err(AuthError {
reason: format!("Login failed. Check your refresh token. Response: {:?}", s),
})
}
}
let mut json_response: Value = res.json().unwrap();
self.access_token = match json_response["response"]["access_token"].take() {
Value::String(s) => s,
_ => panic!("Failed to get access token."),
};
self.refresh_token = match json_response["response"]["refresh_token"].take() {
Value::String(s) => s,
_ => panic!("Failed to get refresh token."),
};
Ok(())
}
#[inline]
pub fn access_token(&self) -> &String {
&self.access_token
}
#[inline]
pub fn access_token_mut(&mut self) -> &mut String {
&mut self.access_token
}
#[inline]
pub fn refresh_token(&self) -> &String {
&self.refresh_token
}
#[inline]
pub fn refresh_token_mut(&mut self) -> &mut String {
&mut self.refresh_token
}
fn send_auth_request(&self, data: &HashMap<&str, &str>) -> Result<Response, reqwest::Error> {
self.client
.post("https://oauth.secure.pixiv.net/auth/token")
.form(&data)
.send()
}
pub fn bad_words(&self) -> PixivRequestBuilder {
let url = "https://public-api.secure.pixiv.net/v1.1/bad_words.json";
let url = Url::parse(&url).unwrap();
PixivRequestBuilder::new(
self,
Method::GET,
url,
HashMap::default(),
)
}
pub fn work(&self, illust_id: usize) -> PixivRequestBuilder {
let url = format!(
"https://public-api.secure.pixiv.net/v1/works/{}.json",
illust_id
);
let extra_params = [
("image_sizes", "px_128x128,small,medium,large,px_480mw"),
("include_stats", "true"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn user(&self, user_id: usize) -> PixivRequestBuilder {
let url = format!(
"https://public-api.secure.pixiv.net/v1/users/{}.json",
user_id
);
let extra_params = [
("profile_image_sizes", "px_170x170,px_50x50"),
("image_sizes", "px_128x128,small,medium,large,px_480mw"),
("include_stats", "1"),
("include_profile", "1"),
("include_workspace", "1"),
("include_contacts", "1"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn feed(&self) -> PixivRequestBuilder {
let url = "https://public-api.secure.pixiv.net/v1/me/feeds.json";
let extra_params = [
("relation", "all"),
("type", "touch_nottext"),
("show_r18", "1"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn favorite_works(&self) -> PixivRequestBuilder {
let url = "https://public-api.secure.pixiv.net/v1/me/favorite_works.json";
let extra_params = [
("page", "1"),
("per_page", "50"),
("publicity", "public"),
("image_sizes", "px_128x128,px_480mw,large"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn favorite_work_add(&self, work_id: usize) -> PixivRequestBuilder {
let url = "https://public-api.secure.pixiv.net/v1/me/favorite_works.json";
let extra_params = [("publicity", "public")];
let url = Url::parse(&url).unwrap();
let params = extra_params
.iter()
.map(|&(k, v)| (k, v.into()))
.chain(Some(("work_id", work_id.to_string().into())))
.collect();
PixivRequestBuilder::new(
self,
Method::POST,
url,
params,
)
}
pub fn favorite_works_remove<B, I>(&self, work_ids: I) -> PixivRequestBuilder
where
B: Borrow<usize>,
I: IntoIterator<Item = B>,
{
let url = "https://public-api.secure.pixiv.net/v1/me/favorite_works.json";
let extra_params = [("publicity", "public")];
let url = Url::parse(&url).unwrap();
let params = extra_params
.iter()
.map(|&(k, v)| (k, v.into()))
.chain(Some(("ids", comma_delimited(work_ids).into())))
.collect();
PixivRequestBuilder::new(
self,
Method::DELETE,
url,
params,
)
}
pub fn following_works(&self) -> PixivRequestBuilder {
let url = "https://public-api.secure.pixiv.net/v1/me/following/works.json";
let extra_params = [
("page", "1"),
("per_page", "30"),
("image_sizes", "px_128x128,px480mw,large"),
("include_stats", "true"),
("include_sanity_level", "true"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn following(&self) -> PixivRequestBuilder {
let url = "https://public-api.secure.pixiv.net/v1/me/following.json";
let extra_params = [("page", "1"), ("per_page", "30"), ("publicity", "public")];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn following_add(&self, user_id: usize) -> PixivRequestBuilder {
let url = "https://public-api.secure.pixiv.net/v1/me/favorite-users.json";
let extra_params = [("publicity", "public")];
let url = Url::parse(&url).unwrap();
let params = extra_params
.iter()
.map(|&(k, v)| (k, v.into()))
.chain(Some(("target_user_id", user_id.to_string().into())))
.collect();
PixivRequestBuilder::new(
self,
Method::POST,
url,
params,
)
}
pub fn following_remove<B, I>(&self, user_ids: I) -> PixivRequestBuilder
where
B: Borrow<usize>,
I: IntoIterator<Item = B>,
{
let url = "https://public-api.secure.pixiv.net/v1/me/favorite-users.json";
let extra_params = [("publicity", "public")];
let url = Url::parse(&url).unwrap();
let params = extra_params
.iter()
.map(|&(k, v)| (k, v.into()))
.chain(Some(("delete_ids", comma_delimited(user_ids).into())))
.collect();
PixivRequestBuilder::new(
self,
Method::DELETE,
url,
params,
)
}
pub fn user_works(&self, user_id: usize) -> PixivRequestBuilder {
let url = format!(
"https://public-api.secure.pixiv.net/v1/users/{}/works.json",
user_id
);
let extra_params = [
("page", "1"),
("per_page", "30"),
("image_sizes", "px_128x128,px480mw,large"),
("include_stats", "true"),
("include_sanity_level", "true"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn user_favorite_works(&self, user_id: usize) -> PixivRequestBuilder {
let url = format!(
"https://public-api.secure.pixiv.net/v1/users/{}/favorite_works.json",
user_id
);
let extra_params = [
("page", "1"),
("per_page", "30"),
("image_sizes", "px_128x128,px480mw,large"),
("include_sanity_level", "true"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn user_feed(&self, user_id: usize) -> PixivRequestBuilder {
let url = format!(
"https://public-api.secure.pixiv.net/v1/users/{}/feeds.json",
user_id
);
let extra_params = [
("relation", "all"),
("type", "touch_nottext"),
("show_r18", "1"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn user_following(&self, user_id: usize) -> PixivRequestBuilder {
let url = format!(
"https://public-api.secure.pixiv.net/v1/users/{}/following.json",
user_id
);
let extra_params = [("page", "1"), ("per_page", "30")];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn ranking(&self, ranking_type: RankingType) -> PixivRequestBuilder {
let url = format!(
"https://public-api.secure.pixiv.net/v1/ranking/{}.json",
ranking_type.as_str()
);
let extra_params = [
("mode", "daily"),
("page", "1"),
("per_page", "50"),
("include_stats", "True"),
("include_sanity_level", "True"),
("image_sizes", "px_128x128,small,medium,large,px_480mw"),
("profile_image_sizes", "px_170x170,px_50x50"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn search_works<'a, V>(&'a self, query: V) -> PixivRequestBuilder<'a>
where
Cow<'a, str>: From<V>,
{
let url = "https://public-api.secure.pixiv.net/v1/search/works.json";
let extra_params = [
("page", "1"),
("per_page", "30"),
("mode", "text"),
("period", "all"),
("order", "desc"),
("sort", "date"),
("types", "illustration,manga,ugoira"),
("include_stats", "true"),
("include_sanity_level", "true"),
("image_sizes", "px_128x128,px480mw,large"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params
.iter()
.map(|&(k, v)| (k, v.into()))
.chain(Some(("q", query.into())))
.collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn latest_works(&self) -> PixivRequestBuilder {
let url = "https://public-api.secure.pixiv.net/v1/works.json";
let extra_params = [
("page", "1"),
("per_page", "30"),
("include_stats", "true"),
("include_sanity_level", "true"),
("image_sizes", "px_128x128,px480mw,large"),
("profile_image_sizes", "px_170x170,px_50x50"),
];
let url = Url::parse(&url).unwrap();
let params = extra_params.iter().map(|&(k, v)| (k, v.into())).collect();
PixivRequestBuilder::new(self, Method::GET, url, params)
}
pub fn execute(&self, request: PixivRequest) -> Result<Response, reqwest::Error> {
self.client.request(request.method, request.url)
.headers(request.headers)
.bearer_auth(self.access_token.clone())
.send()
}
}
impl PixivRequest {
#[inline]
pub fn new(method: Method, url: Url, headers: HeaderMap) -> PixivRequest {
PixivRequest {
method,
url,
headers,
}
}
#[inline]
pub fn method(&self) -> &Method {
&self.method
}
#[inline]
pub fn method_mut(&mut self) -> &mut Method {
&mut self.method
}
#[inline]
pub fn url(&self) -> &Url {
&self.url
}
#[inline]
pub fn url_mut(&mut self) -> &Url {
&mut self.url
}
#[inline]
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
#[inline]
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
fn extend_query_pairs<I, K, V>(&mut self, params: I)
where
I: IntoIterator,
I::Item: Borrow<(K, V)>,
K: AsRef<str>,
V: AsRef<str>,
{
self.url.query_pairs_mut().extend_pairs(params);
}
}
impl<'a> PixivRequestBuilder<'a> {
pub fn new(
pixiv: &'a Pixiv,
method: Method,
url: Url,
params: HashMap<&'a str, Cow<'a, str>>,
) -> PixivRequestBuilder<'a> {
let mut headers = HeaderMap::new();
headers.insert(header::REFERER, header::HeaderValue::from_static("http://spapi.pixiv.net/"));
PixivRequestBuilder {
pixiv,
request: PixivRequest::new(method, url, headers),
params,
}
}
#[inline]
pub fn page(self, value: usize) -> PixivRequestBuilder<'a> {
self.raw_param("page", value.to_string())
}
#[inline]
pub fn per_page(self, value: usize) -> PixivRequestBuilder<'a> {
self.raw_param("value", value.to_string())
}
#[inline]
pub fn max_id(self, value: usize) -> PixivRequestBuilder<'a> {
self.raw_param("max_id", value.to_string())
}
#[inline]
pub fn image_sizes(self, values: &[&str]) -> PixivRequestBuilder<'a> {
self.raw_param("image_sizes", comma_delimited::<&str, _, _>(values))
}
#[inline]
pub fn profile_image_sizes(self, values: &[&str]) -> PixivRequestBuilder<'a> {
self.raw_param("profile_image_sizes", comma_delimited::<&str, _, _>(values))
}
#[inline]
pub fn publicity(self, value: Publicity) -> PixivRequestBuilder<'a> {
self.raw_param("publicity", value.as_str())
}
#[inline]
pub fn show_r18(self, value: bool) -> PixivRequestBuilder<'a> {
if value {
self.raw_param("show_r18", "1")
} else {
self.raw_param("show_r18", "0")
}
}
#[inline]
pub fn include_stats(self, value: bool) -> PixivRequestBuilder<'a> {
if value {
self.raw_param("include_stats", "true")
} else {
self.raw_param("include_stats", "false")
}
}
#[inline]
pub fn include_sanity_level(self, value: bool) -> PixivRequestBuilder<'a> {
if value {
self.raw_param("include_sanity_level", "true")
} else {
self.raw_param("include_sanity_level", "false")
}
}
#[inline]
pub fn ranking_mode(self, value: RankingMode) -> PixivRequestBuilder<'a> {
self.raw_param("mode", value.as_str())
}
pub fn date<V>(self, value: V) -> PixivRequestBuilder<'a>
where
Cow<'a, str>: From<V>,
{
let value: Cow<_> = value.into();
NaiveDate::parse_from_str(&*value, "%Y-%m-%d").expect("Invalid date or format given.");
self.raw_param::<Cow<_>>("date", value)
}
#[inline]
pub fn search_period(self, value: SearchPeriod) -> PixivRequestBuilder<'a> {
self.raw_param("period", value.as_str())
}
#[inline]
pub fn search_mode(self, value: SearchMode) -> PixivRequestBuilder<'a> {
self.raw_param("mode", value.as_str())
}
#[inline]
pub fn search_order(self, value: SearchOrder) -> PixivRequestBuilder<'a> {
self.raw_param("order", value.as_str())
}
pub fn search_sort<V>(self, value: V) -> PixivRequestBuilder<'a>
where
Cow<'a, str>: From<V>,
{
self.raw_param("sort", value)
}
#[inline]
pub fn search_types(self, values: &[&str]) -> PixivRequestBuilder<'a> {
self.raw_param("types", comma_delimited::<&str, _, _>(values))
}
fn raw_param<V>(mut self, key: &'a str, value: V) -> PixivRequestBuilder<'a>
where
Cow<'a, str>: From<V>,
{
self.params.insert(key, value.into());
self
}
#[inline]
pub fn build(mut self) -> PixivRequest {
self.request.extend_query_pairs(&self.params);
self.request
}
pub fn send(self) -> Result<Response, reqwest::Error> {
let pixiv = self.pixiv;
let req = self.build();
trace!("Request URL: {}", req.url);
pixiv.execute(req)
}
}
fn comma_delimited<T, B, I>(iter: I) -> String
where
T: fmt::Display + ?Sized,
B: Borrow<T>,
I: IntoIterator<Item = B>,
{
let mut iter = iter.into_iter();
let mut ret = String::new();
if let Some(b) = iter.next() {
write!(ret, "{}", b.borrow()).unwrap();
for b in iter {
write!(ret, ",{}", b.borrow()).unwrap();
}
}
ret
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_login() {
let client = Client::new();
let mut pixiv: Pixiv = Pixiv::new(&client);
kankyo::load().unwrap();
pixiv
.login(
&kankyo::key("PIXIV_ID").expect("PIXIV_ID isn't set!"),
&kankyo::key("PIXIV_PW").expect("PIXIV_PW isn't set!"),
)
.expect("Failed to log in.");
}
#[test]
fn test_refresh_auth() {
let client = Client::new();
let mut pixiv: Pixiv = Pixiv::new(&client);
kankyo::load().unwrap();
pixiv
.login(
&kankyo::key("PIXIV_ID").expect("PIXIV_ID isn't set!"),
&kankyo::key("PIXIV_PW").expect("PIXIV_PW isn't set!"),
)
.expect("Failed to log in.");
pixiv
.refresh_auth()
.expect("Failed to refresh access token");
}
#[test]
fn test_bad_words() {
let client = Client::new();
let mut pixiv: Pixiv = Pixiv::new(&client);
kankyo::load().unwrap();
pixiv
.login(
&kankyo::key("PIXIV_ID").expect("PIXIV_ID isn't set!"),
&kankyo::key("PIXIV_PW").expect("PIXIV_PW isn't set!"),
)
.expect("Failed to log in.");
let bad_words: Value = pixiv
.bad_words()
.send()
.expect("Request failed.")
.json()
.expect("Failed to parse as json.");
println!("{}", bad_words);
}
#[test]
fn test_work() {
let client = Client::new();
let mut pixiv: Pixiv = Pixiv::new(&client);
kankyo::load().unwrap();
pixiv
.login(
&kankyo::key("PIXIV_ID").expect("PIXIV_ID isn't set!"),
&kankyo::key("PIXIV_PW").expect("PIXIV_PW isn't set!"),
)
.expect("Failed to log in.");
let work: Value = pixiv
.work(66024340)
.send()
.expect("Request failed.")
.json()
.expect("Failed to parse as json.");
println!("{}", work);
}
#[test]
fn test_user() {
let client = Client::new();
let mut pixiv: Pixiv = Pixiv::new(&client);
kankyo::load().unwrap();
pixiv
.login(
&kankyo::key("PIXIV_ID").expect("PIXIV_ID isn't set!"),
&kankyo::key("PIXIV_PW").expect("PIXIV_PW isn't set!"),
)
.expect("Failed to log in.");
let following_works: Value = pixiv
.user(6996493)
.send()
.expect("Request failed.")
.json()
.expect("Failed to parse as json.");
println!("{}", following_works);
}
#[test]
fn test_following_works() {
let client = Client::new();
let mut pixiv: Pixiv = Pixiv::new(&client);
kankyo::load().unwrap();
pixiv
.login(
&kankyo::key("PIXIV_ID").expect("PIXIV_ID isn't set!"),
&kankyo::key("PIXIV_PW").expect("PIXIV_PW isn't set!"),
)
.expect("Failed to log in.");
let following_works: Value = pixiv
.following_works()
.image_sizes(&["large"])
.include_sanity_level(false)
.send()
.expect("Request failed.")
.json()
.expect("Failed to parse as json.");
println!("{}", following_works);
}
#[test]
fn test_into_iterator() {
let client = Client::new();
let pixiv = Pixiv::new(&client);
let slice: &[usize] = &[0, 1, 2];
let vec = slice.to_owned();
let iter = vec.clone().into_iter().chain(Some(3));
pixiv.favorite_works_remove(slice);
pixiv.favorite_works_remove(vec.clone());
pixiv.favorite_works_remove(iter.clone());
pixiv.following_remove(slice);
pixiv.following_remove(vec);
pixiv.following_remove(iter);
}
#[test]
#[should_panic]
fn test_login_fail() {
let client = Client::new();
let mut pixiv: Pixiv = Pixiv::new(&client);
pixiv.login("", "").expect("Failed to log in.");
}
}