aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mssola@mssola.com>2026-01-21 22:05:10 +0100
committerMiquel Sabaté Solà <mssola@mssola.com>2026-01-21 22:05:10 +0100
commitcab0faea15c270dcefd2d88fc1f91477eca00c58 (patch)
treec677165db9f6fb416c451056b82fd52acd1452db
parent8499c476f9a1097b0a7aebe688c6d98d4b7d227b (diff)
downloadmihi-cab0faea15c270dcefd2d88fc1f91477eca00c58.tar.gz
mihi-cab0faea15c270dcefd2d88fc1f91477eca00c58.zip
words: add the --tag flag to the ls command
Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
-rw-r--r--crates/cli/src/inflection.rs2
-rw-r--r--crates/cli/src/words.rs57
-rw-r--r--lib/mihi/src/lib.rs53
3 files changed, 90 insertions, 22 deletions
diff --git a/crates/cli/src/inflection.rs b/crates/cli/src/inflection.rs
index 5db76d4..5aaf61b 100644
--- a/crates/cli/src/inflection.rs
+++ b/crates/cli/src/inflection.rs
@@ -123,7 +123,7 @@ mod tests {
use mihi::DeclensionTable;
fn get_word(enunciated: &str) -> Word {
- let words = mihi::select_enunciated(Some(enunciated.to_string())).unwrap();
+ let words = mihi::select_enunciated(Some(enunciated.to_string()), &[]).unwrap();
assert_eq!(words.len(), 1);
diff --git a/crates/cli/src/words.rs b/crates/cli/src/words.rs
index db8280c..9210dd8 100644
--- a/crates/cli/src/words.rs
+++ b/crates/cli/src/words.rs
@@ -69,6 +69,7 @@ fn help(msg: Option<&str>) {
println!("Options:");
println!(" -h, --help\t\tPrint this message.");
+ println!(" -t, --tag <NAME>\tFilter words which match the given tag NAME. Multiple tags can be provided to match words with any of the tags provided. This will only be accounted in the 'ls' command.");
println!("\nSubcommands:");
println!(" create\t\tCreate a new word.");
@@ -416,7 +417,7 @@ fn create(args: IntoIter<String>) -> i32 {
// Now we try to fetch whether the word already existed, by doing a
// general search on the database.
- let mut words = match mihi::select_enunciated(Some(enunciated.clone())) {
+ let mut words = match mihi::select_enunciated(Some(enunciated.clone()), &[]) {
Ok(words) => words,
Err(e) => {
println!("error: words: {e}");
@@ -455,13 +456,13 @@ fn create(args: IntoIter<String>) -> i32 {
}
}
-fn ls(mut args: IntoIter<String>) -> i32 {
+fn ls(mut args: IntoIter<String>, tags: &[String]) -> i32 {
if args.len() > 1 {
help(Some("error: words: too many filters"));
return 1;
}
- let words = match mihi::select_enunciated(args.next()) {
+ let words = match mihi::select_enunciated(args.next(), tags) {
Ok(words) => words,
Err(e) => {
println!("error: words: {e}");
@@ -480,7 +481,7 @@ fn ls(mut args: IntoIter<String>) -> i32 {
// multiple words match the same search parameter, then the user is asked to
// select one from a list of candidates.
fn select_single_word(search: Option<String>) -> Result<String, String> {
- let words = mihi::select_enunciated(search)?;
+ let words = mihi::select_enunciated(search, &[])?;
match words.len() {
0 => Err("not found".to_string()),
@@ -784,13 +785,31 @@ pub fn run(args: Vec<String>) {
}
let mut it = args.into_iter();
+ let mut do_ls = false;
+ let mut tags = vec![];
- match it.next() {
- Some(first) => match first.as_str() {
+ while let Some(first) = it.next() {
+ match first.as_str() {
"-h" | "--help" => {
help(None);
std::process::exit(0);
}
+ "-t" | "--tag" => match it.next() {
+ Some(t) => {
+ let name = t.trim().to_string();
+ if let Ok(results) = mihi::select_tag_names(&Some(name.clone())) {
+ if results.is_empty() {
+ println!("warning: words: the tag '{}' does not exist.", name);
+ } else {
+ tags.push(name)
+ }
+ }
+ }
+ None => {
+ help(Some("error: words: you have to provide a tag name"));
+ std::process::exit(1);
+ }
+ },
"create" => {
std::process::exit(create(it));
}
@@ -798,7 +817,9 @@ pub fn run(args: Vec<String>) {
std::process::exit(edit(it));
}
"ls" => {
- std::process::exit(ls(it));
+ // 'ls' cannot be executed directly as it might receive extra
+ // parameters to it.
+ do_ls = true;
}
"poke" => {
std::process::exit(poke(it));
@@ -815,16 +836,22 @@ pub fn run(args: Vec<String>) {
));
std::process::exit(1);
}
- },
- None => {
- help(Some(
- "error: words: you need to provide a command"
- .to_string()
- .as_str(),
- ));
- std::process::exit(1);
}
}
+
+ // If 'ls' was asked, do it now as we potentially have all the tags that
+ // were provided by the user. Otherwise, the above loop did not result in a
+ // valid subcommand (it was not even provided).
+ if do_ls {
+ std::process::exit(ls(it, &tags));
+ } else {
+ help(Some(
+ "error: words: you need to provide a command"
+ .to_string()
+ .as_str(),
+ ));
+ std::process::exit(1);
+ }
}
#[cfg(test)]
diff --git a/lib/mihi/src/lib.rs b/lib/mihi/src/lib.rs
index f563645..6ce450e 100644
--- a/lib/mihi/src/lib.rs
+++ b/lib/mihi/src/lib.rs
@@ -664,23 +664,64 @@ pub fn update_timestamp(enunciated: &str) -> Result<(), String> {
}
}
-pub fn select_enunciated(filter: Option<String>) -> Result<Vec<String>, String> {
+/// Select words based on the given `filter` for the enunciated column, which
+/// can be further filtered out by providing a set of `tags`. The words selected
+/// must then have any of the given tags provided by this vector, and it will be
+/// ignored if the passed vector is empty.
+pub fn select_enunciated(filter: Option<String>, tags: &[String]) -> Result<Vec<String>, String> {
let conn = get_connection()?;
let mut stmt;
let mut it = match filter {
Some(filter) => {
- stmt = conn
+ stmt = if tags.is_empty() {
+ conn
.prepare(
"SELECT enunciated FROM words WHERE enunciated LIKE ('%' || ?1 || '%') ORDER BY enunciated",
)
- .unwrap();
+ .unwrap()
+ } else {
+ conn.prepare(
+ format!(
+ "SELECT w.enunciated \
+ FROM words w \
+ JOIN tag_associations ta ON w.id = ta.word_id \
+ JOIN tags t ON t.id = ta.tag_id \
+ WHERE w.enunciated LIKE ('%' || ?1 || '%') AND t.name IN ({}) \
+ ORDER BY w.enunciated",
+ tags.iter()
+ .map(|t| format!("'{}'", t))
+ .collect::<Vec<_>>()
+ .join(", "),
+ )
+ .as_str(),
+ )
+ .unwrap()
+ };
stmt.query([filter.as_str()]).unwrap()
}
None => {
- stmt = conn
- .prepare("SELECT enunciated FROM words ORDER BY enunciated")
- .unwrap();
+ stmt = if tags.is_empty() {
+ conn.prepare("SELECT enunciated FROM words ORDER BY enunciated")
+ .unwrap()
+ } else {
+ conn.prepare(
+ format!(
+ "SELECT w.enunciated \
+ FROM words w \
+ JOIN tag_associations ta ON w.id = ta.word_id \
+ JOIN tags t ON t.id = ta.tag_id \
+ WHERE t.name IN ({}) \
+ ORDER BY w.enunciated",
+ tags.iter()
+ .map(|t| format!("'{}'", t))
+ .collect::<Vec<_>>()
+ .join(", "),
+ )
+ .as_str(),
+ )
+ .unwrap()
+ };
stmt.query([]).unwrap()
}
};