use anyhow::{Context, Result};
use serde::Deserialize;
use std::time::Duration;
use crate::pricing::{self, ModelPrice};
pub const DEFAULT_URL: &str =
"https://raw.githubusercontent.com/Codycody31/tokr/master/data/pricing.toml";
#[derive(Debug, Deserialize)]
struct PricingFile {
#[serde(rename = "model")]
models: Vec<ModelPrice>,
}
pub fn run(url: Option<String>) -> Result<()> {
let url = url.unwrap_or_else(|| DEFAULT_URL.to_string());
println!("Fetching pricing from {url} ...");
let agent = ureq::AgentBuilder::new()
.timeout(Duration::from_secs(20))
.user_agent(concat!("tokr/", env!("CARGO_PKG_VERSION")))
.build();
let resp = agent
.get(&url)
.call()
.with_context(|| format!("GET {url}"))?;
if resp.status() >= 400 {
anyhow::bail!("fetch failed: HTTP {}", resp.status());
}
let body = resp.into_string().context("reading response body")?;
let parsed: PricingFile = toml::from_str(&body).context("parsing fetched TOML")?;
if parsed.models.is_empty() {
anyhow::bail!("fetched file has no [[model]] entries");
}
let n_total = parsed.models.len();
let added = pricing::append_overrides(&parsed.models).context("writing overrides file")?;
println!(
"Fetched {n_total} entries; appended {added} new entries to {}",
pricing::overrides_path()?.display()
);
if added > 0 {
println!(
"Existing rows are still costed against their stamped pricing_version. \
Run `tokr recost` to re-stamp historical events using the new prices."
);
} else {
println!("No new entries — your local pricing is already up to date.");
}
Ok(())
}