use std::error::Error;
use libpkgx::{config::Config, inventory, pantry_db};
use rusqlite::{params, Connection};
use serde::Serialize;
use crate::{
args::Flags,
resolve::{parse_pkgspec, Pkgspec},
};
#[derive(Serialize)]
struct QueryResult {
project: String,
programs: Vec<String>,
}
pub async fn query(
args: &Vec<String>,
flags: &Flags,
conn: &Connection,
config: &Config,
) -> Result<(), Box<dyn Error>> {
let is_json = flags.json == Some(2);
let silent = flags.silent;
if args.is_empty() {
if is_json {
let mut stmt =
conn.prepare("SELECT DISTINCT project FROM provides ORDER BY project")?;
let mut rows = stmt.query(params![])?;
let mut results = Vec::new();
while let Some(row) = rows.next()? {
let project: String = row.get(0)?;
let programs = get_programs(conn, &project)?;
results.push(QueryResult { project, programs });
}
println!("{}", serde_json::to_string_pretty(&results)?);
} else {
let mut stmt = conn.prepare("SELECT program FROM provides")?;
let mut rows = stmt.query(params![])?;
while let Some(row) = rows.next()? {
let program: String = row.get(0)?;
println!("{}", program);
}
}
return Ok(());
}
let mut results = Vec::new();
let mut fail = false;
for arg in args {
let mut pkgspec = parse_pkgspec(arg)?;
let projects = match &mut pkgspec {
Pkgspec::Req(req) if !req.project.contains('.') && req.constraint.raw == "*" => {
pantry_db::which(&req.project, conn)?
}
Pkgspec::Req(req) => match resolve_project(&req.project, conn) {
Ok(project) => {
req.project = project.clone();
vec![project]
}
Err(e) => {
if silent {
std::process::exit(1);
}
println!("{}", e);
fail = true;
continue;
}
},
Pkgspec::Latest(name) => match resolve_project(name, conn) {
Ok(project) => vec![project],
Err(e) => {
if silent {
std::process::exit(1);
}
println!("{}", e);
fail = true;
continue;
}
},
};
if projects.is_empty() {
if silent {
std::process::exit(1);
}
println!("{} not found", arg);
fail = true;
continue;
}
if let Pkgspec::Req(req) = &pkgspec {
if req.constraint.raw != "*" {
let versions = inventory::ls(&projects[0], config).await?;
if !versions.iter().any(|v| req.constraint.satisfies(v)) {
if silent {
std::process::exit(1);
}
println!(
"no versions matching {} found for {}",
req.constraint.raw, projects[0]
);
fail = true;
continue;
}
}
}
if is_json {
for project in &projects {
let programs = get_programs(conn, project)?;
results.push(QueryResult {
project: project.clone(),
programs,
});
}
} else if !silent {
println!("{}", projects.join(", "));
}
}
if is_json {
println!("{}", serde_json::to_string_pretty(&results)?);
}
if fail {
std::process::exit(1);
}
Ok(())
}
fn resolve_project(input: &str, conn: &Connection) -> Result<String, Box<dyn Error>> {
let projects = pantry_db::which(&input.to_string(), conn)?;
match projects.len() {
1 => Ok(projects[0].clone()),
0 => {
if input.contains('.') {
let mut stmt = conn.prepare("SELECT COUNT(*) FROM provides WHERE project = ?")?;
let count: i64 = stmt.query_row(params![input], |row| row.get(0))?;
if count > 0 {
return Ok(input.to_string());
}
}
Err(format!("{} not found", input).into())
}
_ => Err(format!("{} is ambiguous: {}", input, projects.join(", ")).into()),
}
}
fn get_programs(conn: &Connection, project: &str) -> Result<Vec<String>, Box<dyn Error>> {
let mut stmt =
conn.prepare("SELECT program FROM provides WHERE project = ? ORDER BY program")?;
let mut rows = stmt.query(params![project])?;
let mut programs = Vec::new();
while let Some(row) = rows.next()? {
programs.push(row.get(0)?);
}
Ok(programs)
}