Skip to content

Commit acfc1bb

Browse files
authored
feat: interface reindex_applications() (#704)
* feat: impl re-indexing applications * drop pizza engine
1 parent c4d178d commit acfc1bb

File tree

4 files changed

+179
-82
lines changed

4 files changed

+179
-82
lines changed

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,4 @@ tauri-plugin-global-shortcut = "2"
125125
tauri-plugin-updater = { git = "https://github.com/infinilabs/plugins-workspace", branch = "v2" }
126126

127127
[target."cfg(target_os = \"windows\")".dependencies]
128-
enigo="0.3"
128+
enigo="0.3"

src-tauri/src/extension/built_in/application/with_feature.rs

Lines changed: 163 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use pizza_engine::store::{DiskStore, DiskStoreSnapshot};
2525
use pizza_engine::writer::Writer;
2626
use pizza_engine::{doc, Engine, EngineBuilder};
2727
use serde_json::Value as Json;
28+
use std::path::Path;
2829
use std::path::PathBuf;
2930
use tauri::{async_runtime, AppHandle, Manager, Runtime};
3031
use tauri_plugin_fs_pro::{icon, metadata, name, IconOptions};
@@ -47,6 +48,8 @@ const TAURI_STORE_APP_ALIAS: &str = "app_alias";
4748
const TAURI_STORE_KEY_SEARCH_PATH: &str = "search_path";
4849
const TAURI_STORE_KEY_DISABLED_APP_LIST: &str = "disabled_app_list";
4950

51+
const INDEX_DIR: &str = "local_application_index";
52+
5053
/// We use this as:
5154
///
5255
/// 1. querysource ID
@@ -209,6 +212,86 @@ impl SearchSourceState for ApplicationSearchSourceState {
209212
}
210213
}
211214

215+
/// Index applications if they have not been indexed (by checking if `app_index_dir` exists).
216+
async fn index_applications_if_not_indexed<R: Runtime>(
217+
tauri_app_handle: &AppHandle<R>,
218+
app_index_dir: &Path,
219+
) -> anyhow::Result<ApplicationSearchSourceState> {
220+
let index_exists = app_index_dir.exists();
221+
222+
let mut pizza_engine_builder = EngineBuilder::new();
223+
let disk_store = DiskStore::new(&app_index_dir)?;
224+
pizza_engine_builder.set_data_store(disk_store);
225+
226+
let mut schema = Schema::new();
227+
let field_app_name = Property::builder(FieldType::Text).build();
228+
schema
229+
.add_property(FIELD_APP_NAME, field_app_name)
230+
.expect("no collision could happen");
231+
let property_icon = Property::builder(FieldType::Text).index(false).build();
232+
schema
233+
.add_property(FIELD_ICON_PATH, property_icon)
234+
.expect("no collision could happen");
235+
schema
236+
.add_property(FIELD_APP_ALIAS, Property::as_text(None))
237+
.expect("no collision could happen");
238+
schema.freeze();
239+
pizza_engine_builder.set_schema(schema);
240+
241+
let pizza_engine = pizza_engine_builder
242+
.build()
243+
.unwrap_or_else(|e| panic!("failed to build Pizza engine due to [{}]", e));
244+
pizza_engine.start();
245+
let mut writer = pizza_engine.acquire_writer();
246+
247+
if !index_exists {
248+
let default_search_path = get_default_search_paths();
249+
let apps = list_app_in(default_search_path).map_err(|str| anyhow::anyhow!(str))?;
250+
251+
for app in apps.iter() {
252+
let app_path = get_app_path(app);
253+
let app_name = get_app_name(app).await;
254+
let app_icon_path = get_app_icon_path(&tauri_app_handle, app)
255+
.await
256+
.map_err(|str| anyhow::anyhow!(str))?;
257+
let app_alias = get_app_alias(&tauri_app_handle, &app_path).unwrap_or(String::new());
258+
259+
if app_name.is_empty() || app_name.eq(&tauri_app_handle.package_info().name) {
260+
continue;
261+
}
262+
263+
// You cannot write `app_name.clone()` within the `doc!()` macro, we should fix this.
264+
let app_name_clone = app_name.clone();
265+
let app_path_clone = app_path.clone();
266+
let document = doc!( app_path_clone, {
267+
FIELD_APP_NAME => app_name_clone,
268+
FIELD_ICON_PATH => app_icon_path,
269+
FIELD_APP_ALIAS => app_alias,
270+
}
271+
);
272+
273+
// We don't error out because one failure won't break the whole thing
274+
if let Err(e) = writer.create_document(document).await {
275+
warn!(
276+
"failed to index application [app name: '{}', app path: '{}'] due to error [{}]", app_name, app_path, e
277+
)
278+
}
279+
}
280+
281+
writer.commit()?;
282+
}
283+
284+
let snapshot = pizza_engine.create_snapshot();
285+
let searcher = pizza_engine.acquire_searcher();
286+
287+
Ok(ApplicationSearchSourceState {
288+
searcher,
289+
snapshot,
290+
engine: pizza_engine,
291+
writer,
292+
})
293+
}
294+
212295
/// Upon application start, index all the applications found in the `get_default_search_paths()`.
213296
struct IndexAllApplicationsTask<R: Runtime> {
214297
tauri_app_handle: AppHandle<R>,
@@ -228,87 +311,47 @@ impl<R: Runtime> Task for IndexAllApplicationsTask<R> {
228311
.path()
229312
.app_data_dir()
230313
.expect("failed to find the local dir");
231-
app_index_dir.push("local_application_index");
232-
233-
let index_exists = app_index_dir.exists();
234-
235-
let mut pizza_engine_builder = EngineBuilder::new();
236-
let disk_store = task_exec_try!(DiskStore::new(&app_index_dir), callback);
237-
pizza_engine_builder.set_data_store(disk_store);
238-
239-
let mut schema = Schema::new();
240-
let field_app_name = Property::builder(FieldType::Text).build();
241-
schema
242-
.add_property(FIELD_APP_NAME, field_app_name)
243-
.expect("no collision could happen");
244-
let property_icon = Property::builder(FieldType::Text).index(false).build();
245-
schema
246-
.add_property(FIELD_ICON_PATH, property_icon)
247-
.expect("no collision could happen");
248-
schema
249-
.add_property(FIELD_APP_ALIAS, Property::as_text(None))
250-
.expect("no collision could happen");
251-
schema.freeze();
252-
pizza_engine_builder.set_schema(schema);
253-
254-
let pizza_engine = pizza_engine_builder
255-
.build()
256-
.unwrap_or_else(|e| panic!("failed to build Pizza engine due to [{}]", e));
257-
pizza_engine.start();
258-
let mut writer = pizza_engine.acquire_writer();
259-
260-
if !index_exists {
261-
let default_search_path = get_default_search_paths();
262-
let apps = task_exec_try!(list_app_in(default_search_path), callback);
263-
264-
for app in apps.iter() {
265-
let app_path = get_app_path(app);
266-
let app_name = get_app_name(app).await;
267-
let app_icon_path = task_exec_try!(
268-
get_app_icon_path(&self.tauri_app_handle, app).await,
269-
callback
270-
);
271-
let app_alias =
272-
get_app_alias(&self.tauri_app_handle, &app_path).unwrap_or(String::new());
273-
274-
if app_name.is_empty() || app_name.eq(&self.tauri_app_handle.package_info().name) {
275-
continue;
276-
}
277-
278-
// You cannot write `app_name.clone()` within the `doc!()` macro, we should fix this.
279-
let app_name_clone = app_name.clone();
280-
let app_path_clone = app_path.clone();
281-
let document = doc!( app_path_clone, {
282-
FIELD_APP_NAME => app_name_clone,
283-
FIELD_ICON_PATH => app_icon_path,
284-
FIELD_APP_ALIAS => app_alias,
285-
}
286-
);
287-
288-
// We don't error out because one failure won't break the whole thing
289-
if let Err(e) = writer.create_document(document).await {
290-
warn!(
291-
"failed to index application [app name: '{}', app path: '{}'] due to error [{}]", app_name, app_path, e
292-
)
293-
}
294-
}
314+
app_index_dir.push(INDEX_DIR);
315+
let app_search_source_state = task_exec_try!(
316+
index_applications_if_not_indexed(&self.tauri_app_handle, &app_index_dir).await,
317+
callback
318+
);
319+
*state = Some(Box::new(app_search_source_state));
320+
callback.send(Ok(())).expect("rx dropped");
321+
}
322+
}
295323

296-
task_exec_try!(writer.commit(), callback);
297-
}
324+
struct ReindexAllApplicationsTask<R: Runtime> {
325+
tauri_app_handle: AppHandle<R>,
326+
callback: Option<tokio::sync::oneshot::Sender<Result<(), String>>>,
327+
}
298328

299-
let snapshot = pizza_engine.create_snapshot();
300-
let searcher = pizza_engine.acquire_searcher();
329+
#[async_trait::async_trait(?Send)]
330+
impl<R: Runtime> Task for ReindexAllApplicationsTask<R> {
331+
fn search_source_id(&self) -> &'static str {
332+
APPLICATION_SEARCH_SOURCE_ID
333+
}
301334

302-
let state_to_store = Box::new(ApplicationSearchSourceState {
303-
searcher,
304-
snapshot,
305-
engine: pizza_engine,
306-
writer,
307-
}) as Box<dyn SearchSourceState>;
335+
async fn exec(&mut self, state: &mut Option<Box<dyn SearchSourceState>>) {
336+
let callback = self.callback.take().unwrap();
308337

309-
*state = Some(state_to_store);
338+
// Clear the state
339+
*state = None;
340+
let mut app_index_dir = self
341+
.tauri_app_handle
342+
.path()
343+
.app_data_dir()
344+
.expect("failed to find the local dir");
345+
app_index_dir.push(INDEX_DIR);
346+
task_exec_try!(tokio::fs::remove_dir_all(&app_index_dir).await, callback);
310347

311-
callback.send(Ok(())).unwrap();
348+
// Then re-index the apps
349+
let app_search_source_state = task_exec_try!(
350+
index_applications_if_not_indexed(&self.tauri_app_handle, &app_index_dir).await,
351+
callback
352+
);
353+
*state = Some(Box::new(app_search_source_state));
354+
callback.send(Ok(())).expect("rx dropped");
312355
}
313356
}
314357

@@ -326,6 +369,23 @@ impl<R: Runtime> Task for SearchApplicationsTask<R> {
326369

327370
async fn exec(&mut self, state: &mut Option<Box<dyn SearchSourceState>>) {
328371
let callback = self.callback.take().unwrap();
372+
373+
let Some(state) = state.as_mut() else {
374+
let empty_hits = SearchResult {
375+
tracing_id: String::new(),
376+
explains: None,
377+
total_hits: 0,
378+
hits: None,
379+
};
380+
381+
let rx_dropped_error = callback.send(Ok(empty_hits)).is_err();
382+
if rx_dropped_error {
383+
warn!("failed to send local app search result back because the corresponding channel receiver end has been unexpected dropped, which could happen due to a low query timeout")
384+
}
385+
386+
return;
387+
};
388+
329389
let disabled_app_list = get_disabled_app_list(&self.tauri_app_handle);
330390

331391
// TODO: search via alias, implement this when Pizza engine supports update
@@ -344,8 +404,6 @@ impl<R: Runtime> Task for SearchApplicationsTask<R> {
344404
"{{ \"query\": {{ \"bool\": {{ \"should\": [ {{ \"match\": {{ \"{FIELD_APP_NAME}\": {:?} }} }}, {{ \"prefix\": {{ \"{FIELD_APP_NAME}\": {:?} }} }} ] }} }} }}", self.query_string, self.query_string);
345405

346406
let state = state
347-
.as_mut()
348-
.expect("should be set before")
349407
.as_mut_any()
350408
.downcast_mut::<ApplicationSearchSourceState>()
351409
.unwrap();
@@ -1105,3 +1163,30 @@ pub async fn get_app_metadata(app_name: String, app_path: String) -> Result<AppM
11051163
last_opened,
11061164
})
11071165
}
1166+
1167+
#[tauri::command]
1168+
pub async fn reindex_applications<R: Runtime>(
1169+
tauri_app_handle: AppHandle<R>,
1170+
) -> Result<(), String> {
1171+
let (tx, rx) = tokio::sync::oneshot::channel();
1172+
let reindex_applications_task = ReindexAllApplicationsTask {
1173+
tauri_app_handle: tauri_app_handle.clone(),
1174+
callback: Some(tx),
1175+
};
1176+
1177+
RUNTIME_TX
1178+
.get()
1179+
.unwrap()
1180+
.send(Box::new(reindex_applications_task))
1181+
.unwrap();
1182+
1183+
let reindexing_applications_result = rx.await.unwrap();
1184+
if let Err(ref e) = reindexing_applications_result {
1185+
error!(
1186+
"re-indexing local applications failed, app search won't work, error [{}]",
1187+
e
1188+
)
1189+
}
1190+
1191+
reindexing_applications_result
1192+
}

src-tauri/src/extension/built_in/application/without_feature.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ pub(crate) const QUERYSOURCE_ID_DATASOURCE_ID_DATASOURCE_NAME: &str = "Applicati
1212
pub struct ApplicationSearchSource;
1313

1414
impl ApplicationSearchSource {
15-
pub async fn prepare_index_and_store<R: Runtime>(_app_handle: AppHandle<R>) -> Result<(), String> {
15+
pub async fn prepare_index_and_store<R: Runtime>(
16+
_app_handle: AppHandle<R>,
17+
) -> Result<(), String> {
1618
Ok(())
1719
}
1820
}
@@ -118,13 +120,22 @@ pub async fn get_app_metadata<R: Runtime>(
118120
unreachable!("app list should be empty, there is no way this can be invoked")
119121
}
120122

121-
122123
pub(crate) fn set_apps_hotkey<R: Runtime>(_tauri_app_handle: &AppHandle<R>) -> Result<(), String> {
123124
// no-op
124125
Ok(())
125126
}
126127

127-
pub(crate) fn unset_apps_hotkey<R: Runtime>(_tauri_app_handle: &AppHandle<R>) -> Result<(), String> {
128+
pub(crate) fn unset_apps_hotkey<R: Runtime>(
129+
_tauri_app_handle: &AppHandle<R>,
130+
) -> Result<(), String> {
131+
// no-op
132+
Ok(())
133+
}
134+
135+
#[tauri::command]
136+
pub async fn reindex_applications<R: Runtime>(
137+
_tauri_app_handle: AppHandle<R>,
138+
) -> Result<(), String> {
128139
// no-op
129140
Ok(())
130141
}

src-tauri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ pub fn run() {
149149
extension::built_in::application::get_app_metadata,
150150
extension::built_in::application::add_app_search_path,
151151
extension::built_in::application::remove_app_search_path,
152+
extension::built_in::application::reindex_applications,
152153
extension::list_extensions,
153154
extension::enable_extension,
154155
extension::disable_extension,

0 commit comments

Comments
 (0)