paiagram/
search.rs

1use crate::settings::Settings;
2use bevy::ecs::message::MessageId;
3use bevy::prelude::*;
4use ib_matcher::matcher::{IbMatcher, PinyinMatchConfig, RomajiMatchConfig};
5use ib_matcher::pinyin::PinyinNotation;
6use rayon::prelude::*;
7use std::sync::Arc;
8
9pub struct SearchPlugin;
10impl Plugin for SearchPlugin {
11    fn build(&self, app: &mut App) {
12        app.add_message::<SearchCommand>()
13            .add_message::<SearchResponse>()
14            .add_systems(Update, search.run_if(on_message::<SearchCommand>));
15    }
16}
17
18#[derive(Message)]
19pub enum SearchCommand {
20    Table {
21        data: Arc<Vec<String>>,
22        query: String,
23    },
24}
25
26#[derive(Message)]
27pub enum SearchResponse {
28    Table(MessageId<SearchCommand>, Vec<usize>),
29}
30
31pub fn search(
32    mut msg_reader: MessageReader<SearchCommand>,
33    mut msg_writer: MessageWriter<SearchResponse>,
34    settings: Res<Settings>,
35) {
36    let now = instant::Instant::now();
37    for (msg, id) in msg_reader.read_with_id() {
38        match msg {
39            SearchCommand::Table { data, query } => {
40                let result = search_table(data, &build_matcher(query, &settings));
41                msg_writer.write(SearchResponse::Table(id, result));
42            }
43        }
44    }
45    debug!("Search completed in {:?}", now.elapsed());
46}
47
48fn build_matcher(query: &str, settings: &Settings) -> IbMatcher<'static> {
49    let now = instant::Instant::now();
50    let matcher = IbMatcher::builder(query)
51        .pinyin(PinyinMatchConfig::notations(
52            PinyinNotation::Ascii | PinyinNotation::DiletterMicrosoft,
53        ))
54        .maybe_romaji(
55            settings
56                .enable_romaji_search
57                .then(RomajiMatchConfig::default),
58        )
59        .build();
60    debug!("Matcher built in {:?}", now.elapsed());
61    matcher
62}
63
64fn search_table(data: &Arc<Vec<String>>, searcher: &IbMatcher<'static>) -> Vec<usize> {
65    data.par_iter()
66        .enumerate()
67        .filter_map(|(index, row)| {
68            if searcher.is_match(row.as_str()) {
69                None
70            } else {
71                Some(index)
72            }
73        })
74        .collect()
75}