use anyhow::{Context, Result};
use clap::Args;
use crate::db::Database;
use crate::output::{Format, VariantPrinter};
use crate::variant::{AnnotatedVariant, VariantQuery};
const DEFAULT_API_URL: &str = "https://api.genome.sh";
#[derive(Args)]
pub struct QueryArgs {
#[arg(required = true, num_args = 1..)]
pub queries: Vec<String>,
#[arg(short, long, default_value = "human")]
pub format: Format,
#[arg(long)]
pub local: bool,
#[arg(long, env = "GENOME_API_URL", default_value = DEFAULT_API_URL)]
pub api_url: String,
}
impl QueryArgs {
pub async fn run(self) -> Result<()> {
let printer = VariantPrinter::new(self.format.clone());
for raw in &self.queries {
let results = if self.local {
self.query_local(raw)?
} else {
self.query_api(raw).await?
};
if results.is_empty() {
eprintln!("No results for: {raw}");
continue;
}
for variant in &results {
printer.print(variant)?;
}
}
Ok(())
}
fn query_local(&self, raw: &str) -> Result<Vec<AnnotatedVariant>> {
let db = Database::open()
.context("No local database. Run `genome db install` or remove --local.")?;
let query = VariantQuery::parse(raw)?;
db.query(&query)
}
async fn query_api(&self, raw: &str) -> Result<Vec<AnnotatedVariant>> {
let url = format!("{}/v1/query/{}", self.api_url, urlencoding::encode(raw));
let response = reqwest::get(&url).await.with_context(|| {
format!(
"Failed to reach API at {}. Use --local for offline mode.",
self.api_url
)
})?;
if !response.status().is_success() {
let status = response.status();
let body = response.text().await.unwrap_or_default();
anyhow::bail!("API returned {status}: {body}");
}
let variants: Vec<AnnotatedVariant> = response
.json()
.await
.context("Failed to parse API response")?;
Ok(variants)
}
}