This commit is contained in:
JMARyA 2024-02-13 09:12:16 +01:00
parent 8d8d90efd1
commit 8ea359d6df
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
5 changed files with 243 additions and 208 deletions

View file

@ -1,6 +1,117 @@
use clap::{arg, command, ArgMatches};
pub fn get_args() -> ArgMatches {
use crate::quit_err;
pub struct Args {
pub root_dir: String,
pub output_json: bool,
pub no_header: bool,
pub limit: usize,
pub offset: usize,
pub ignoretags: bool,
pub sort_by: Option<String>,
pub group_by: Option<String>,
pub reversed: bool,
pub columns: Vec<String>,
pub headers: Vec<String>,
pub filters: serde_json::Value,
}
pub fn get_args() -> Args {
let args = get_args_match();
let root_dir = args.get_one::<String>("dir").unwrap();
let output_json = args.get_flag("json");
let no_header = args.get_flag("noheader");
let limit: usize = args
.get_one::<String>("limit")
.unwrap()
.parse()
.unwrap_or_else(|e| quit_err(e, "Limit is not a number"));
let offset: usize = args
.get_one::<String>("offset")
.unwrap()
.parse()
.unwrap_or_else(|e| quit_err(e, "Offset is not a number"));
let ignoretags: bool = args.get_flag("ignoretags");
let sort_by = args
.get_one::<String>("sortby")
.map(std::borrow::ToOwned::to_owned);
let group_by = args
.get_one::<String>("groupby")
.map(std::borrow::ToOwned::to_owned);
let reversed = args.get_flag("reverse");
let columns: Vec<_> = args
.get_many::<String>("column")
.unwrap()
.cloned()
.collect();
log::debug!("columns: {columns:?}");
let (columns, headers): (Vec<_>, Vec<_>) = columns
.into_iter()
.map(|x| {
let (column, header_rename) = x.split_once(':').unwrap_or((&x, &x));
(column.to_owned(), header_rename.to_owned())
})
.unzip();
if columns != headers {
log::debug!("renamed headers: {headers:?}");
}
let filters = args
.get_many::<String>("filter")
.map_or_else(std::vec::Vec::new, std::iter::Iterator::collect);
log::debug!("raw filters: {filters:?}");
let filters = if filters.len() == 1 {
let filter = filters.first().unwrap();
serde_json::from_str(filter)
.unwrap_or_else(|e| quit_err(e, &format!("filter '{filter}' could not be parsed")))
} else {
let filters: Vec<_> = filters
.iter()
.map(|x| {
serde_json::from_str::<serde_json::Value>(x)
.unwrap_or_else(|e| quit_err(e, &format!("filter '{x}' could not be parsed")))
})
.collect();
serde_json::json!({
"$and": filters
})
};
log::debug!("parsed filters: {filters:?}");
Args {
root_dir: root_dir.to_string(),
output_json,
no_header,
limit,
offset,
ignoretags,
sort_by,
group_by,
reversed,
columns,
headers,
filters,
}
}
fn get_args_match() -> ArgMatches {
command!()
.about("Query markdown files")
.arg(arg!([dir] "Directory to scan").required(true))

View file

@ -106,7 +106,7 @@ impl Index {
.as_mapping()
.unwrap()
.get("tags")
.map(|x| x.as_sequence().unwrap().clone())
.map(|x| x.as_sequence().unwrap_or(&Vec::new()).clone())
.unwrap_or_default();
let inline_tags = get_inline_tags(&content);
@ -123,6 +123,7 @@ impl Index {
.insert("tags".into(), unique_tags.into_iter().collect());
}
log::trace!("Adding {path} to Index");
let doc = Document { path, frontmatter };
i.documents.push(doc);
}
@ -149,15 +150,17 @@ impl Index {
scope.reverse();
}
let scope: Vec<_> = scope.into_iter().skip(offset).collect();
let scope = scope.into_iter().skip(offset);
let scope = if limit == 0 {
scope
if limit == 0 {
Self {
documents: scope.collect(),
}
} else {
scope.into_iter().take(limit).collect()
};
Self { documents: scope }
Self {
documents: scope.take(limit).collect(),
}
}
}
#[must_use]

View file

@ -4,98 +4,41 @@ use mdq::Index;
mod args;
pub fn quit_err(e: impl std::error::Error, msg: &str) -> ! {
eprintln!("Error: {msg}. {e}");
std::process::exit(1);
}
fn main() {
env_logger::init();
let args = args::get_args();
let root_dir = args.get_one::<String>("dir").unwrap();
let output_json = args.get_flag("json");
let no_header = args.get_flag("noheader");
let limit: usize = args.get_one::<String>("limit").unwrap().parse().unwrap();
let offset: usize = args.get_one::<String>("offset").unwrap().parse().unwrap();
let ignoretags: bool = args.get_flag("ignoretags");
let sort_by = args
.get_one::<String>("sortby")
.map(std::borrow::ToOwned::to_owned);
let group_by = args
.get_one::<String>("groupby")
.map(std::borrow::ToOwned::to_owned);
let reversed = args.get_flag("reverse");
let columns: Vec<_> = args
.get_many::<String>("column")
.unwrap()
.cloned()
.collect();
log::debug!("columns: {columns:?}");
let (columns, headers): (Vec<_>, Vec<_>) = columns
.into_iter()
.map(|x| {
let (column, header_rename) = x.split_once(':').unwrap_or((&x, &x));
(column.to_owned(), header_rename.to_owned())
})
.unzip();
if columns != headers {
log::debug!("renamed headers: {headers:?}");
let mut i = Index::new(&args.root_dir, args.ignoretags);
if !args.filters.is_null() {
i = i.filter_documents(&args.filters);
}
let filters = args
.get_many::<String>("filter")
.map_or_else(std::vec::Vec::new, std::iter::Iterator::collect);
i = i.apply(args.limit, args.offset, args.sort_by, args.reversed);
log::debug!("raw filters: {filters:?}");
let filters = if filters.len() == 1 {
serde_json::from_str(filters.first().unwrap()).unwrap()
} else {
let filters: Vec<_> = filters
.iter()
.map(|x| serde_json::from_str::<serde_json::Value>(x).unwrap())
.collect();
serde_json::json!({
"$and": filters
})
};
log::debug!("parsed filters: {filters:?}");
let mut i = Index::new(root_dir, ignoretags);
if !filters.is_null() {
i = i.filter_documents(&filters);
}
i = i.apply(limit, offset, sort_by, reversed);
if group_by.is_some() {
let grouped = i.group_by(&group_by.clone().unwrap());
if args.group_by.is_some() {
let grouped = i.group_by(&args.group_by.clone().unwrap());
let grouped: HashMap<_, _> = grouped
.into_iter()
.map(|(key, val)| (key, val.create_table_data(&columns)))
.map(|(key, val)| (key, val.create_table_data(&args.columns)))
.collect();
if output_json {
if args.output_json {
let mut data = serde_json::json!(
{
"columns": columns,
"groupby": group_by.unwrap(),
"columns": args.columns,
"groupby": args.group_by.unwrap(),
"results": grouped
}
);
if columns != headers {
if args.columns != args.headers {
data.as_object_mut()
.unwrap()
.insert("headers".into(), headers.into());
.insert("headers".into(), args.headers.into());
}
println!("{}", serde_json::to_string(&data).unwrap());
return;
@ -111,13 +54,20 @@ fn main() {
});
for group in grouped_keys {
println!("# {group}");
print_result(grouped.get(group).unwrap().clone(), &headers);
print_result(grouped.get(group).unwrap().clone(), &args.headers);
}
} else {
let mut first = true;
for (_, val) in grouped {
if first {
print_csv(val, if no_header { None } else { Some(&headers) });
print_csv(
val,
if args.no_header {
None
} else {
Some(&args.headers)
},
);
first = false;
continue;
}
@ -127,27 +77,34 @@ fn main() {
return;
}
let data = i.create_table_data(&columns);
let data = i.create_table_data(&args.columns);
if output_json {
if args.output_json {
let mut data = serde_json::json!(
{
"columns": columns,
"columns": args.columns,
"results": data
}
);
if columns != headers {
if args.columns != args.headers {
data.as_object_mut()
.unwrap()
.insert("headers".into(), headers.into());
.insert("headers".into(), args.headers.into());
}
println!("{}", serde_json::to_string(&data).unwrap());
return;
}
if std::io::stdout().is_terminal() {
print_result(data, &headers);
print_result(data, &args.headers);
} else {
print_csv(data, if no_header { None } else { Some(&headers) });
print_csv(
data,
if args.no_header {
None
} else {
Some(&args.headers)
},
);
}
}