use crate::error::{Error, Result};
use crate::wordnet::{self, fetch, WordNet};
pub fn fetch_langs(languages: &[String]) -> Result<()> {
if languages.is_empty() {
return Err(Error::Config("usage: inkhaven wordnet fetch <lang>… (e.g. `en`)".into()));
}
let rt = tokio::runtime::Runtime::new()
.map_err(|e| Error::Config(format!("could not start async runtime: {e}")))?;
for lang in languages {
eprint!("fetching {lang} wordnet … ");
let wn = rt.block_on(fetch::fetch(lang)).map_err(Error::Config)?;
let path = wordnet::index_path(lang)
.ok_or_else(|| Error::Config("could not resolve the user data directory".into()))?;
wn.save(&path).map_err(Error::Store)?;
eprintln!(
"{} lemmas · {} synsets → {}",
wn.lemma_count(),
wn.synset_count(),
path.display()
);
}
Ok(())
}
pub fn import(lang: &str, path: &std::path::Path) -> Result<()> {
let wn = fetch::import_file(path).map_err(Error::Config)?;
let out = wordnet::index_path(lang)
.ok_or_else(|| Error::Config("could not resolve the user data directory".into()))?;
wn.save(&out).map_err(Error::Store)?;
eprintln!(
"imported {lang} wordnet · {} lemmas · {} synsets → {}",
wn.lemma_count(),
wn.synset_count(),
out.display()
);
Ok(())
}
pub fn lookup(word: &str, language: Option<&str>) -> Result<()> {
let lang = language.unwrap_or("en");
let path = wordnet::index_path(lang)
.ok_or_else(|| Error::Config("could not resolve the user data directory".into()))?;
if !path.exists() {
return Err(Error::Config(format!(
"no `{lang}` wordnet installed — run `inkhaven wordnet fetch {lang}` first"
)));
}
let wn = WordNet::load(&path).map_err(Error::Store)?;
let pivot = if lang.eq_ignore_ascii_case("en") {
None
} else {
match wordnet::index_path("en").filter(|p| p.exists()) {
Some(p) => match WordNet::load(&p) {
Ok(wn) => Some(wn),
Err(e) => {
eprintln!("note: English pivot unavailable ({e}); hypernyms/hyponyms won't expand");
None
}
},
None => None,
}
};
let result = match pivot.as_ref() {
Some(p) => wn.lookup_with_pivot(word, Some(p)),
None => wn.lookup(word),
};
if result.is_empty() {
println!("`{word}` — not found in the {lang} wordnet");
return Ok(());
}
println!("{word} · {lang}");
for (i, s) in result.senses.iter().enumerate() {
println!("\n {}. {}", i + 1, s.pos);
if let Some(def) = &s.definition {
if !def.is_empty() {
println!(" {def}");
}
}
let row = |label: &str, items: &[String]| {
if !items.is_empty() {
println!(" {label:<10} {}", items.join(", "));
}
};
row("synonyms", &s.synonyms);
row("antonyms", &s.antonyms);
row("hypernyms", &s.hypernyms);
row("hyponyms", &s.hyponyms);
}
Ok(())
}
pub fn list() -> Result<()> {
let installed = |lang: &str| -> &'static str {
if wordnet::index_path(lang).map(|p| p.exists()).unwrap_or(false) {
"installed"
} else {
"—"
}
};
println!("WordNet sources (fetch with `inkhaven wordnet fetch <lang>`):\n");
for src in fetch::SOURCES {
println!(" {:<3} {:<11} {:<34} {}", src.lang, installed(src.lang), src.name, src.license);
}
println!(
" {:<3} {:<11} {:<34} {}",
"ru", installed("ru"), "RuWordNet — import your own", "research licence"
);
println!(
"\n Russian has no open distribution — obtain a WN-LMF file under its licence and run\n \
`inkhaven wordnet import ru <file.xml>`. A non-English lookup expands its\n \
hypernyms/hyponyms through English, so `fetch en` too.",
);
if let Some(dir) = wordnet::data_dir() {
println!("\ndata directory: {}", dir.display());
}
Ok(())
}