40 lines
1.3 KiB
Rust
40 lines
1.3 KiB
Rust
//! Product data adapters. The deal source is a user-pasted product URL — no
|
|
//! per-retailer scrapers. Adapters are generic platform readers; the first is
|
|
//! Shopify, whose storefronts expose a public `/products/{handle}.json` document.
|
|
|
|
use rust_decimal::Decimal;
|
|
|
|
mod shopify;
|
|
|
|
/// Normalised product snapshot produced by an adapter.
|
|
#[derive(Debug, Clone)]
|
|
pub struct FetchedProduct {
|
|
pub title: String,
|
|
pub price: Decimal,
|
|
pub currency: String,
|
|
pub image_url: Option<String>,
|
|
pub in_stock: Option<bool>,
|
|
pub source: &'static str,
|
|
}
|
|
|
|
/// A shared HTTP client tuned for storefront fetches.
|
|
pub fn http_client() -> reqwest::Client {
|
|
reqwest::Client::builder()
|
|
.user_agent("consumers-bot/0.1 (+self-hosted wantlist price watcher)")
|
|
.timeout(std::time::Duration::from_secs(15))
|
|
.build()
|
|
.expect("failed to build reqwest client")
|
|
}
|
|
|
|
/// Try every adapter in turn. Returns the first that recognises the URL.
|
|
pub async fn fetch_product(
|
|
client: &reqwest::Client,
|
|
url: &str,
|
|
default_currency: &str,
|
|
) -> anyhow::Result<FetchedProduct> {
|
|
if let Some(p) = shopify::fetch(client, url, default_currency).await? {
|
|
return Ok(p);
|
|
}
|
|
anyhow::bail!("no adapter could read this URL (only Shopify storefronts are supported for now)")
|
|
}
|