use clap::Subcommand;
use tail_fin_common::TailFinError;
use crate::session::{browser_session, launch_stealth_session, print_json, print_list, Ctx};
#[derive(Subcommand)]
pub enum CoupangAction {
Search {
query: String,
#[arg(long, default_value_t = 20)]
limit: usize,
#[arg(long, default_value_t = 1)]
page: usize,
#[arg(long)]
sort: Option<String>,
#[arg(long)]
rocket_only: bool,
#[arg(long)]
region: Option<String>,
},
Detail {
product_id: String,
#[arg(long)]
region: Option<String>,
},
Cart {
#[arg(long)]
region: Option<String>,
},
AddToCart {
product_id: String,
#[arg(long)]
region: Option<String>,
},
}
async fn coupang_session(
ctx: &Ctx,
region: Option<&str>,
) -> Result<night_fury_core::BrowserSession, TailFinError> {
let start_url = tail_fin_coupang::base_url_for_region(region);
if let Some(ref host) = ctx.connect {
browser_session(host, ctx.headed).await
} else {
eprintln!("Launching stealth browser for Coupang...");
launch_stealth_session(&start_url, ctx.headed).await
}
}
pub async fn run(action: CoupangAction, ctx: &Ctx) -> Result<(), TailFinError> {
match action {
CoupangAction::Search {
query,
limit,
page,
sort,
rocket_only,
region,
} => {
if let Some(ref cookies_flag) = ctx.cookies {
let path = if cookies_flag == "auto" {
crate::session::default_cookies_path("coupang")
} else {
std::path::PathBuf::from(cookies_flag)
};
let client = tail_fin_coupang::CoupangHttpClient::from_cookie_file(
&path,
region.as_deref(),
)?;
let products = client
.search(&query, limit, page, sort.as_deref(), rocket_only)
.await?;
print_list("products", &products, products.len())?;
} else {
let session = coupang_session(ctx, region.as_deref()).await?;
let client = tail_fin_coupang::CoupangClient::new(session);
let products = client
.search(&query, limit, page, sort.as_deref(), rocket_only)
.await?;
print_list("products", &products, products.len())?;
}
}
CoupangAction::Detail { product_id, region } => {
let session = coupang_session(ctx, region.as_deref()).await?;
let client = tail_fin_coupang::CoupangClient::new(session);
let detail = client.detail(&product_id).await?;
print_json(&detail)?;
}
CoupangAction::Cart { region } => {
let session = coupang_session(ctx, region.as_deref()).await?;
let client = tail_fin_coupang::CoupangClient::new(session);
let items = client.cart().await?;
print_list("cart_items", &items, items.len())?;
}
CoupangAction::AddToCart { product_id, region } => {
let session = coupang_session(ctx, region.as_deref()).await?;
let client = tail_fin_coupang::CoupangClient::new(session);
let result = client.add_to_cart(&product_id).await?;
print_json(&result)?;
}
}
Ok(())
}
pub struct Adapter;
impl crate::adapter::CliAdapter for Adapter {
fn name(&self) -> &'static str {
"coupang"
}
fn about(&self) -> &'static str {
"Coupang e-commerce operations"
}
fn command(&self) -> clap::Command {
<CoupangAction as clap::Subcommand>::augment_subcommands(
clap::Command::new("coupang").about("Coupang e-commerce operations"),
)
}
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 = <CoupangAction as clap::FromArgMatches>::from_arg_matches(matches)
.map_err(|e| tail_fin_common::TailFinError::Api(e.to_string()))?;
run(action, ctx).await
})
}
}