use std::path::Path;
use dbmd_core::query::Query;
use dbmd_core::store::Layer;
use dbmd_core::{IndexRecord, Store};
use crate::cli::QueryArgs;
use crate::context::Context;
use crate::error::{CliError, CliResult, ExitCode};
pub fn run(ctx: &Context, args: &QueryArgs) -> CliResult {
let store = open_store(&args.dir)?;
let query = build_query(args)?;
let mut records = query.execute(&store).map_err(map_store_error)?;
records.sort_by(|a, b| a.path.cmp(&b.path));
if let Some(limit) = args.limit {
records.truncate(limit);
}
if ctx.json {
print!("{}", records_json(&records));
} else {
print!("{}", records_text(&records));
}
Ok(())
}
fn build_query(args: &QueryArgs) -> Result<Query, CliError> {
let mut query = Query::new();
if let Some(t) = &args.r#type {
query = query.with_type(t);
}
if let Some(layer_name) = &args.r#in {
query = query.with_layer(parse_layer(layer_name)?);
}
for clause in &args.r#where {
let (key, value) = split_where(clause)?;
query = query.with_where(key, value);
}
Ok(query)
}
fn parse_layer(name: &str) -> Result<Layer, CliError> {
Layer::from_dir_name(name).ok_or_else(|| {
CliError::new(
ExitCode::Runtime,
"BAD_LAYER",
format!("unknown layer `{name}`"),
)
.with_hint("layer must be one of: sources, records, wiki")
})
}
fn split_where(clause: &str) -> Result<(&str, &str), CliError> {
clause.split_once('=').ok_or_else(|| {
CliError::new(
ExitCode::Runtime,
"BAD_WHERE",
format!("`--where` clause `{clause}` is not `key=value`"),
)
.with_hint("write the filter as `key=value`, e.g. --where status=active")
})
}
fn open_store(dir: &str) -> Result<Store, CliError> {
Store::open(Path::new(dir)).map_err(|e| CliError::from(dbmd_core::Error::from(e)))
}
fn map_store_error(err: dbmd_core::StoreError) -> CliError {
CliError::from(dbmd_core::Error::from(err))
}
fn records_text(records: &[IndexRecord]) -> String {
let mut out = String::new();
for r in records {
out.push_str(&r.path.to_string_lossy());
out.push('\n');
}
out
}
fn records_json(records: &[IndexRecord]) -> String {
let mut s = serde_json::to_string_pretty(records).unwrap_or_else(|_| "[]".to_string());
s.push('\n');
s
}