Yakudatsu/src/kanji_search.rs
2023-12-17 16:31:05 +00:00

161 lines
6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::{
io::{stdin, stdout, Write},
path::PathBuf,
collections::HashSet,
};
use colored::*;
use kradical_parsing::radk;
pub fn search_by_radical(query: &mut String){
let mut result: HashSet<_> = HashSet::new();
let mut aux: HashSet<_> = HashSet::new();
let path = get_radkfile_path();
match radk::parse_file(path.unwrap()) { /* if it doesn't exist, just panic */
Ok(radk_list) => {
result.clear();
/* First iteration: get the baseline for the results */
let mut rad = query.chars().nth(1).unwrap();
if rad == '*' || rad == '' {
/* if search_by_strokes returned an error then something is very wrong */
rad = search_by_strokes(query, &radk_list, 1).expect("Couldn't parse input");
}
for k in radk_list.iter() {
if k.radical.glyph.contains(rad) {
for input in &k.kanji {
result.insert(input);
}
break;
}
}
/* Iterate until you've exhausted user input: refine the baseline to get final output */
for (i, mut rad) in query.clone().chars().skip(2).enumerate() {
if rad == '*' || rad == '' {
/* if search_by_strokes returned an error then something is very wrong */
rad = search_by_strokes(query, &radk_list, i+2).expect("Couldn't parse input");
}
for k in radk_list.iter() {
if k.radical.glyph.contains(rad) {
for input in &k.kanji {
aux.insert(input);
}
result = &result & &aux;
aux.clear();
break;
}
}
}
for r in result {
print!("{r} ");
}
println!();
}
Err(_e) => eprintln!("Error while reading radkfile\nIf you don't have the radkfile, download it from \
https://www.edrdg.org/krad/kradinf.html and place it in \"~/.local/share/\" on Linux or \"~\\AppData\\Local\\\" on Windows. \
This file is needed to search radicals by strokes."),
}
}
fn search_by_strokes(query: &mut String, radk_list: &[radk::Membership], n: usize) -> Result<char, std::io::Error> {
let mut strokes = String::new();
let mut radicals: Vec<char> = Vec::new();
let rad;
loop{
print!("How many strokes does your radical have? ");
stdout().flush()?;
strokes.clear();
if stdin().read_line(&mut strokes)? == 0{
std::process::exit(0);
}
match strokes.trim().parse::<u8>() {
Ok(strk) => {
let mut i = 1;
for k in radk_list.iter() {
if k.radical.strokes == strk {
print!("{}{} ", i, k.radical.glyph);
radicals.push(k.radical.glyph.chars().next().unwrap());
i += 1;
} else if k.radical.strokes > strk {
println!();
break;
}
}
loop {
print!("Choose the radical to use for your search: ");
stdout().flush()?;
strokes.clear();
if stdin().read_line(&mut strokes)? == 0{
std::process::exit(0);
}
match strokes.trim().parse::<usize>() {
Ok(strk) => {
if strk < 1 || strk > i-1 {
eprintln!("Couldn't parse input: number not in range");
} else {
rad = radicals.get(strk-1).unwrap();
/* UTF-8 is not fun */
let char_and_index = query.char_indices().nth(n).unwrap();
query.replace_range(char_and_index.0..
char_and_index.0 +
char_and_index.1.len_utf8(),
rad.to_string().as_str());
println!("{}", query.as_str().bright_black());
return Ok(*rad);
}
},
Err(e) => { eprintln!("{e}"); }
}
}
},
Err(e) => { eprintln!("{e}") }
}
}
}
#[cfg(unix)]
fn get_radkfile_path() -> Option<PathBuf> {
#[allow(deprecated)] /* obviously no windows problem here */
std::env::home_dir()
.map(|path| path.join(".local/share/radkfile"))
}
#[cfg(windows)]
/* Nicked this section straight from https://github.com/rust-lang/cargo/blob/master/crates/home/src/windows.rs */
extern "C" {
fn wcslen(buf: *const u16) -> usize;
}
#[cfg(windows)]
fn get_radkfile_path() -> Option<PathBuf> {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use windows_sys::Win32::Foundation::{MAX_PATH, S_OK};
use windows_sys::Win32::UI::Shell::{SHGetFolderPathW, CSIDL_PROFILE};
match env::var_os("USERPROFILE").filter(|s| !s.is_empty()).map(PathBuf::from) {
Some(path) => {
Some(path.join("Appdata\\Local\\radkfile"))
},
None => {
unsafe {
let mut path: Vec<u16> = Vec::with_capacity(MAX_PATH as usize);
match SHGetFolderPathW(0, CSIDL_PROFILE as i32, 0, 0, path.as_mut_ptr()) {
S_OK => {
let len = wcslen(path.as_ptr());
path.set_len(len);
let s = OsString::from_wide(&path);
Some(PathBuf::from(s).join("Appdata\\Local\\radkfile"))
}
_ => None,
}
}
}
}
}