use std::future::{Future, IntoFuture};
use std::marker::PhantomData;
use std::pin::Pin;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::error::GbizError;
use crate::query::{HojinSearchQuery, UpdateInfoQuery};
use crate::types::{
HojinChildResponse, HojinResponse, SearchResponse, UpdateInfoChildResponse,
UpdateInfoHojinResponse,
};
pub const DEFAULT_BASE_URL: &str = "https://api.info.gbiz.go.jp/hojin";
const API_TOKEN_HEADER: &str = "X-hojinInfo-api-token";
#[derive(Debug, Clone)]
pub struct GbizInfoClient {
http: reqwest::Client,
base_url: String,
api_token: String,
}
impl GbizInfoClient {
pub fn new(api_token: impl Into<String>) -> Self {
Self::builder(api_token).build()
}
pub fn builder(api_token: impl Into<String>) -> GbizInfoClientBuilder {
GbizInfoClientBuilder {
api_token: api_token.into(),
base_url: None,
http: None,
}
}
pub async fn search(&self, query: &HojinSearchQuery) -> Result<SearchResponse, GbizError> {
self.get("/v2/hojin", query).await
}
pub fn hojin(&self, corporate_number: &str) -> DetailRequest<'_, HojinResponse> {
self.detail(corporate_number, "")
}
pub fn certification(&self, corporate_number: &str) -> DetailRequest<'_, HojinChildResponse> {
self.detail(corporate_number, "/certification")
}
pub fn commendation(&self, corporate_number: &str) -> DetailRequest<'_, HojinChildResponse> {
self.detail(corporate_number, "/commendation")
}
pub fn corporation(&self, corporate_number: &str) -> DetailRequest<'_, HojinChildResponse> {
self.detail(corporate_number, "/corporation")
}
pub fn finance(&self, corporate_number: &str) -> DetailRequest<'_, HojinChildResponse> {
self.detail(corporate_number, "/finance")
}
pub fn patent(&self, corporate_number: &str) -> DetailRequest<'_, HojinChildResponse> {
self.detail(corporate_number, "/patent")
}
pub fn procurement(&self, corporate_number: &str) -> DetailRequest<'_, HojinChildResponse> {
self.detail(corporate_number, "/procurement")
}
pub fn subsidy(&self, corporate_number: &str) -> DetailRequest<'_, HojinChildResponse> {
self.detail(corporate_number, "/subsidy")
}
pub fn workplace(&self, corporate_number: &str) -> DetailRequest<'_, HojinChildResponse> {
self.detail(corporate_number, "/workplace")
}
pub async fn update_info(
&self,
query: &UpdateInfoQuery,
) -> Result<UpdateInfoHojinResponse, GbizError> {
self.get("/v2/hojin/updateInfo", query).await
}
pub async fn update_info_certification(
&self,
query: &UpdateInfoQuery,
) -> Result<UpdateInfoChildResponse, GbizError> {
self.get("/v2/hojin/updateInfo/certification", query).await
}
pub async fn update_info_commendation(
&self,
query: &UpdateInfoQuery,
) -> Result<UpdateInfoChildResponse, GbizError> {
self.get("/v2/hojin/updateInfo/commendation", query).await
}
pub async fn update_info_corporation(
&self,
query: &UpdateInfoQuery,
) -> Result<UpdateInfoChildResponse, GbizError> {
self.get("/v2/hojin/updateInfo/corporation", query).await
}
pub async fn update_info_finance(
&self,
query: &UpdateInfoQuery,
) -> Result<UpdateInfoChildResponse, GbizError> {
self.get("/v2/hojin/updateInfo/finance", query).await
}
pub async fn update_info_patent(
&self,
query: &UpdateInfoQuery,
) -> Result<UpdateInfoChildResponse, GbizError> {
self.get("/v2/hojin/updateInfo/patent", query).await
}
pub async fn update_info_procurement(
&self,
query: &UpdateInfoQuery,
) -> Result<UpdateInfoChildResponse, GbizError> {
self.get("/v2/hojin/updateInfo/procurement", query).await
}
pub async fn update_info_subsidy(
&self,
query: &UpdateInfoQuery,
) -> Result<UpdateInfoChildResponse, GbizError> {
self.get("/v2/hojin/updateInfo/subsidy", query).await
}
pub async fn update_info_workplace(
&self,
query: &UpdateInfoQuery,
) -> Result<UpdateInfoChildResponse, GbizError> {
self.get("/v2/hojin/updateInfo/workplace", query).await
}
fn detail<T>(&self, corporate_number: &str, suffix: &str) -> DetailRequest<'_, T> {
DetailRequest {
client: self,
path: format!("/v2/hojin/{corporate_number}{suffix}"),
metadata_flg: None,
_response: PhantomData,
}
}
async fn get<T, Q>(&self, path: &str, query: &Q) -> Result<T, GbizError>
where
T: DeserializeOwned,
Q: Serialize + ?Sized,
{
let response = self
.http
.get(format!("{}{}", self.base_url, path))
.header(API_TOKEN_HEADER, &self.api_token)
.header(reqwest::header::ACCEPT, "application/json")
.query(query)
.send()
.await?;
let status = response.status();
let body = response.text().await?;
if !status.is_success() {
return Err(GbizError::Status {
status: status.as_u16(),
body,
});
}
serde_json::from_str(&body).map_err(|error| GbizError::Decode { error, body })
}
}
#[derive(Debug)]
pub struct GbizInfoClientBuilder {
api_token: String,
base_url: Option<String>,
http: Option<reqwest::Client>,
}
impl GbizInfoClientBuilder {
pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
self.base_url = Some(base_url.into());
self
}
pub fn http_client(mut self, http: reqwest::Client) -> Self {
self.http = Some(http);
self
}
pub fn build(self) -> GbizInfoClient {
GbizInfoClient {
http: self.http.unwrap_or_default(),
base_url: self
.base_url
.unwrap_or_else(|| DEFAULT_BASE_URL.to_string()),
api_token: self.api_token,
}
}
}
#[derive(Debug)]
#[must_use = "リクエストは `.await` するまで送信されません"]
pub struct DetailRequest<'a, T> {
client: &'a GbizInfoClient,
path: String,
metadata_flg: Option<bool>,
_response: PhantomData<T>,
}
impl<'a, T> DetailRequest<'a, T>
where
T: DeserializeOwned,
{
pub fn metadata_flg(mut self, value: bool) -> Self {
self.metadata_flg = Some(value);
self
}
pub async fn send(self) -> Result<T, GbizError> {
let mut query: Vec<(&str, String)> = Vec::new();
if let Some(flg) = self.metadata_flg {
query.push(("metadata_flg", flg.to_string()));
}
self.client.get(&self.path, &query).await
}
}
impl<'a, T> IntoFuture for DetailRequest<'a, T>
where
T: DeserializeOwned + Send + 'a,
{
type Output = Result<T, GbizError>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(self.send())
}
}