use clap::Subcommand;
use tail_fin_common::TailFinError;
use crate::session::{browser_session, print_json, print_list, Ctx};
#[derive(Subcommand)]
pub enum PccAction {
Info,
Search {
query: String,
#[arg(long, default_value_t = 1)]
page: u32,
},
SearchCompany {
query: String,
#[arg(long, default_value_t = 1)]
page: u32,
},
SearchId {
query: String,
#[arg(long, default_value_t = 1)]
page: u32,
},
SearchBudget {
query: String,
#[arg(long, default_value_t = 1)]
page: u32,
},
Budgets,
Date { date: String },
Units,
Unit {
unit_id: String,
#[arg(long, default_value_t = 1)]
page: u32,
},
Tender { unit_id: String, job_number: String },
}
async fn pcc_session(ctx: &Ctx) -> Result<night_fury_core::BrowserSession, TailFinError> {
if let Some(ref host) = ctx.connect {
browser_session(host, ctx.headed).await
} else {
eprintln!("Launching stealth browser for PCC (with Cloudflare bypass)...");
let session = night_fury_core::BrowserSession::builder()
.headed(ctx.headed)
.cloudflare_timeout(std::time::Duration::from_secs(15))
.launch_stealth("https://pcc.g0v.ronny.tw")
.await?;
Ok(session)
}
}
pub async fn run(action: PccAction, ctx: &Ctx) -> Result<(), TailFinError> {
let session = pcc_session(ctx).await?;
let client = tail_fin_pcc::PccClient::new(session);
match action {
PccAction::Info => {
let info = client.info().await?;
print_json(&info)?;
}
PccAction::Search { query, page } => {
let result = client.search(&query, page).await?;
print_json(&result)?;
}
PccAction::SearchCompany { query, page } => {
let result = client.search_company(&query, page).await?;
print_json(&result)?;
}
PccAction::SearchId { query, page } => {
let result = client.search_id(&query, page).await?;
print_json(&result)?;
}
PccAction::SearchBudget { query, page } => {
let result = client.search_budget(&query, page).await?;
print_json(&result)?;
}
PccAction::Budgets => {
let budgets = client.budgets().await?;
print_list("budgets", &budgets, budgets.len())?;
}
PccAction::Date { date } => {
let result = client.date(&date).await?;
print_json(&result)?;
}
PccAction::Units => {
let units = client.units().await?;
print_list("units", &units, units.len())?;
}
PccAction::Unit { unit_id, page } => {
let result = client.unit(&unit_id, page).await?;
print_json(&result)?;
}
PccAction::Tender {
unit_id,
job_number,
} => {
let detail = client.tender(&unit_id, &job_number).await?;
print_json(&detail)?;
}
}
Ok(())
}
pub struct Adapter;
impl crate::adapter::CliAdapter for Adapter {
fn name(&self) -> &'static str {
"pcc"
}
fn about(&self) -> &'static str {
"Taiwan government procurement (PCC)"
}
fn command(&self) -> clap::Command {
<PccAction as clap::Subcommand>::augment_subcommands(
clap::Command::new("pcc").about("Taiwan government procurement (PCC)"),
)
}
fn dispatch<'a>(
&'a self,
matches: &'a clap::ArgMatches,
ctx: &'a crate::session::Ctx,
) -> std::pin::Pin<
Box<
dyn std::future::Future<Output = Result<(), tail_fin_common::TailFinError>> + Send + 'a,
>,
> {
Box::pin(async move {
let action = <PccAction as clap::FromArgMatches>::from_arg_matches(matches)
.map_err(|e| tail_fin_common::TailFinError::Api(e.to_string()))?;
run(action, ctx).await
})
}
}