vndb-api 1.0.3

Fully Functional Visual Novel Database (VNDB) HTTPS API Wrapper
Documentation
use reqwest::Client;

use crate::format::error::VndbApiError;
use crate::request::query::{
    CharacterQuery, ProducerQuery, Query, ReleaseQuery, StaffQuery, TagQuery, TraitQuery,
    UListQuery, VnQuery,
};
use crate::request::response::Response;

use crate::format::auth::AuthInfo;
use crate::format::stats::VndbStats;
use crate::format::user::{UserSearch, UserSearchFields};

use crate::format::character::Character;
use crate::format::producer::Producer;
use crate::format::release::Release;
use crate::format::rlist::RListPatch;
use crate::format::staff::Staff;
use crate::format::tag::Tag;
use crate::format::traits::Trait;
use crate::format::ulist::{UList, UListLabels, UListLabelsFieldChoices, UListPatch};
use crate::format::vn::VisualNovel;

const API_ENDPOINT: &str = "https://api.vndb.org/kana";

pub struct VndbApiClient {
    client: Client,
    access_token: String,
}

impl VndbApiClient {
    pub fn new(token: &String) -> Self {
        VndbApiClient {
            client: Client::new(),
            access_token: token.clone(),
        }
    }

    pub async fn get_stats(&self) -> Result<VndbStats, VndbApiError> {
        let url = format!("{}/stats", API_ENDPOINT);
        let response = self.client.get(&url).send().await?;

        if response.status().is_success() {
            let result = response.json::<VndbStats>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn get_auth_info(&self) -> Result<AuthInfo, VndbApiError> {
        let url = format!("{}/authinfo", API_ENDPOINT);
        let response = self
            .client
            .get(&url)
            .header("Authorization", format!("token {}", self.access_token))
            .send()
            .await?;

        if response.status().is_success() {
            let result = response.json::<AuthInfo>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn get_user(
        &self,
        q: &Vec<String>,
        fields: &UserSearchFields,
    ) -> Result<UserSearch, VndbApiError> {
        let url = format!(
            "{}/user?q={}&fields={}",
            API_ENDPOINT,
            q.join("&q="),
            fields.to_csv()
        );
        let response = self.client.get(&url).json(q).send().await?;

        if response.status().is_success() {
            let result = response.json::<UserSearch>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn vn_search(
        &self,
        q: &Query<VnQuery>,
    ) -> Result<Response<VisualNovel>, VndbApiError> {
        let url = format!("{}/vn", API_ENDPOINT);
        let response = self.client.post(&url).json(q).send().await?;

        if response.status().is_success() {
            let result = response.json::<Response<VisualNovel>>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn release_search(
        &self,
        q: &Query<ReleaseQuery>,
    ) -> Result<Response<Release>, VndbApiError> {
        let url = format!("{}/release", API_ENDPOINT);
        let response = self.client.post(&url).json(q).send().await?;

        if response.status().is_success() {
            let result = response.json::<Response<Release>>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn producer_search(
        &self,
        q: &Query<ProducerQuery>,
    ) -> Result<Response<Producer>, VndbApiError> {
        let url = format!("{}/producer", API_ENDPOINT);
        let response = self.client.post(&url).json(q).send().await?;

        if response.status().is_success() {
            let result = response.json::<Response<Producer>>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn character_search(
        &self,
        q: &Query<CharacterQuery>,
    ) -> Result<Response<Character>, VndbApiError> {
        let url = format!("{}/character", API_ENDPOINT);
        let response = self.client.post(&url).json(q).send().await?;

        if response.status().is_success() {
            let result = response.json::<Response<Character>>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn staff_search(
        &self,
        q: &Query<StaffQuery>,
    ) -> Result<Response<Staff>, VndbApiError> {
        let url = format!("{}/staff", API_ENDPOINT);
        let response = self.client.post(&url).json(q).send().await?;

        if response.status().is_success() {
            let result = response.json::<Response<Staff>>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn tag_search(&self, q: &Query<TagQuery>) -> Result<Response<Tag>, VndbApiError> {
        let url = format!("{}/tag", API_ENDPOINT);
        let response = self.client.post(&url).json(q).send().await?;

        if response.status().is_success() {
            let result = response.json::<Response<Tag>>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn trait_search(
        &self,
        q: &Query<TraitQuery>,
    ) -> Result<Response<Trait>, VndbApiError> {
        let url = format!("{}/trait", API_ENDPOINT);
        let response = self.client.post(&url).json(q).send().await?;

        if response.status().is_success() {
            let result = response.json::<Response<Trait>>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn ulist(&self, q: &Query<UListQuery>) -> Result<Response<UList>, VndbApiError> {
        let url = format!("{}/ulist", API_ENDPOINT);
        let response = self.client.post(&url).json(q).send().await?;

        if response.status().is_success() {
            let result = response.json::<Response<UList>>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn get_ulist_labels(
        &self,
        user: &String,
        fields: &UListLabelsFieldChoices,
    ) -> Result<UListLabels, VndbApiError> {
        let url = format!(
            "{}/ulist_labels?user={}&fields={}",
            API_ENDPOINT,
            user.clone(),
            fields.to_csv()
        );
        let response = self.client.get(&url).send().await?;

        if response.status().is_success() {
            let result = response.json::<UListLabels>().await?;
            Ok(result)
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn ulist_patch(
        &self,
        vn_id: &String,
        patch: &UListPatch,
    ) -> Result<(), VndbApiError> {
        let url = format!("{}/ulist/{}", API_ENDPOINT, vn_id);
        let response = self
            .client
            .patch(&url)
            .header("Authorization", format!("token {}", self.access_token))
            .header("Content-Type", "application/json")
            .json(patch)
            .send()
            .await?;

        if response.status().is_success() {
            Ok(())
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn rlist_patch(&self, r_id: &String, patch: &RListPatch) -> Result<(), VndbApiError> {
        let url = format!("{}/rlist/{}", API_ENDPOINT, r_id);
        let response = self
            .client
            .patch(&url)
            .header("Authorization", format!("token {}", self.access_token))
            .header("Content-Type", "application/json")
            .json(patch)
            .send()
            .await?;

        if response.status().is_success() {
            Ok(())
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn ulist_remove(&self, vn_id: &String) -> Result<(), VndbApiError> {
        let url = format!("{}/ulist/{}", API_ENDPOINT, vn_id);
        let response = self
            .client
            .delete(&url)
            .header("Authorization", format!("token {}", self.access_token))
            .send()
            .await?;

        if response.status().is_success() {
            Ok(())
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }

    pub async fn rlist_remove(&self, r_id: &String) -> Result<(), VndbApiError> {
        let url = format!("{}/rlist/{}", API_ENDPOINT, r_id);
        let response = self
            .client
            .delete(&url)
            .header("Authorization", format!("token {}", self.access_token))
            .send()
            .await?;

        if response.status().is_success() {
            Ok(())
        } else {
            Err(VndbApiError::new(response.status(), response.text().await?))
        }
    }
}