tail-fin-pcc 0.7.5

Taiwan government procurement (PCC) adapter for tail-fin: tender search, company lookup, budget tracking
Documentation
pub mod parsing;
pub mod site;
pub mod types;

use tail_fin_common::page::{browser_origin_fetch, ensure_on_domain};
use tail_fin_common::BrowserSession;
use tail_fin_common::TailFinError;

pub use site::PccSite;
pub use types::*;

const PCC_DOMAINS: &[&str] = &["pcc.g0v.ronny.tw", "pcc-api.openfun.app"];

pub struct PccClient {
    session: BrowserSession,
}

impl PccClient {
    pub fn new(session: BrowserSession) -> Self {
        Self { session }
    }

    async fn api_get(&self, path: &str) -> Result<serde_json::Value, TailFinError> {
        ensure_on_domain(&self.session, PCC_DOMAINS).await?;
        browser_origin_fetch(&self.session, path, None).await
    }

    pub async fn info(&self) -> Result<PccInfo, TailFinError> {
        let data = self.api_get("/api/getinfo").await?;
        parsing::parse_info(&data)
    }

    pub async fn search(&self, query: &str, page: u32) -> Result<SearchResult, TailFinError> {
        let q = urlencoding::encode(query);
        let data = self
            .api_get(&format!("/api/searchbytitle?query={}&page={}", q, page))
            .await?;
        parsing::parse_search_result(&data)
    }

    pub async fn search_company(
        &self,
        query: &str,
        page: u32,
    ) -> Result<SearchResult, TailFinError> {
        let q = urlencoding::encode(query);
        let data = self
            .api_get(&format!(
                "/api/searchbycompanyname?query={}&page={}",
                q, page
            ))
            .await?;
        parsing::parse_search_result(&data)
    }

    pub async fn search_id(&self, query: &str, page: u32) -> Result<SearchResult, TailFinError> {
        let q = urlencoding::encode(query);
        let data = self
            .api_get(&format!("/api/searchbycompanyid?query={}&page={}", q, page))
            .await?;
        parsing::parse_search_result(&data)
    }

    pub async fn search_budget(
        &self,
        query: &str,
        page: u32,
    ) -> Result<SearchResult, TailFinError> {
        let q = urlencoding::encode(query);
        let data = self
            .api_get(&format!(
                "/api/searchbyspecialbudget?query={}&page={}",
                q, page
            ))
            .await?;
        parsing::parse_search_result(&data)
    }

    pub async fn budgets(&self) -> Result<Vec<SpecialBudget>, TailFinError> {
        let data = self.api_get("/api/searchallspecialbudget").await?;
        Ok(parsing::parse_budgets(&data))
    }

    pub async fn date(&self, date: &str) -> Result<SearchResult, TailFinError> {
        let data = self
            .api_get(&format!("/api/listbydate?date={}", date))
            .await?;
        parsing::parse_search_result(&data)
    }

    pub async fn units(&self) -> Result<Vec<Unit>, TailFinError> {
        let data = self.api_get("/api/unit").await?;
        Ok(parsing::parse_units(&data))
    }

    pub async fn unit(&self, unit_id: &str, page: u32) -> Result<SearchResult, TailFinError> {
        let data = self
            .api_get(&format!(
                "/api/listbyunit?unit_id={}&page={}",
                unit_id, page
            ))
            .await?;
        parsing::parse_search_result(&data)
    }

    pub async fn tender(
        &self,
        unit_id: &str,
        job_number: &str,
    ) -> Result<serde_json::Value, TailFinError> {
        self.api_get(&format!(
            "/api/tender?unit_id={}&job_number={}",
            unit_id, job_number
        ))
        .await
    }
}