Skip to main content

Mountain/Track/Effect/CreateEffectForRequest/
Search.rs

1#![allow(non_snake_case, unused_variables, dead_code, unused_imports)]
2
3//! # Search Effect (CreateEffectForRequest)
4//!
5//! Effect constructors for workspace search RPC methods. Handles file and
6//! text search by delegating to `SearchProvider` and `WorkspaceProvider`
7//! traits on `MountainEnvironment`.
8//!
9//! ## Methods handled
10//!
11//! | Method | Description |
12//! |---|---|
13//! | `findFiles` | Glob-based file search using `ignore`-aware walker |
14//! | `findTextInFiles` | Full-text search delegating to `SearchProvider::TextSearch` |
15//! | `Search.TextSearch` | Alternative text search RPC (separate method name) |
16//!
17//! `findFiles` reuses `WorkspaceProvider::FindFilesInWorkspace` to get the
18//! same `ignore`-aware glob walker used by `search:fileSearch`.
19
20use std::{future::Future, pin::Pin, sync::Arc};
21
22use serde_json::{Value, json};
23use tauri::Runtime;
24use CommonLibrary::{
25	Environment::Requires::Requires,
26	Search::SearchProvider::SearchProvider,
27	Workspace::WorkspaceProvider::WorkspaceProvider,
28};
29
30use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, Track::Effect::MappedEffectType::MappedEffect};
31
32pub fn CreateEffect<R:Runtime>(MethodName:&str, Parameters:Value) -> Option<Result<MappedEffect, String>> {
33	match MethodName {
34		"findFiles" | "findTextInFiles" => {
35			let MethodNameOwned = MethodName.to_string();
36
37			let effect =
38				move |run_time:Arc<ApplicationRunTime>| -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
39					Box::pin(async move {
40						let _workspace:Arc<dyn WorkspaceProvider> = run_time.Environment.Require();
41						let provider:Arc<dyn SearchProvider> = run_time.Environment.Require();
42						// Accept three call shapes:
43						//   - `{pattern, options}` named form
44						//   - `[pattern, options]` positional from `TryMountainThenNode` Cocoon path
45						//   - `pattern` bare (legacy single-arg)
46						let Args = if let Some(Object) = Parameters.as_object() {
47							(
48								Object.get("pattern").cloned().unwrap_or_default(),
49								Object.get("options").cloned().unwrap_or_default(),
50							)
51						} else if Parameters.is_array() {
52							(
53								Parameters.get(0).cloned().unwrap_or_default(),
54								Parameters.get(1).cloned().unwrap_or_default(),
55							)
56						} else {
57							(Parameters.clone(), Value::Null)
58						};
59						let (Pattern, Options) = Args;
60						if MethodNameOwned == "findTextInFiles" {
61							return provider.TextSearch(Pattern, Options).await.map_err(|e| e.to_string());
62						}
63						// `findFiles` - delegate to
64						// `WorkspaceProvider::FindFilesInWorkspace` so we
65						// get the same `ignore`-aware glob walker that
66						// `search:fileSearch` uses. The trait returns
67						// `Vec<Url>`; map to `Vec<String>` for the wire.
68						if Pattern.is_null() {
69							return Ok(json!([]));
70						}
71						let Exclude = Options.get("exclude").cloned().filter(|V| !V.is_null());
72						let MaxResults = Options.get("maxResults").and_then(Value::as_u64).map(|N| N as usize);
73						let UseIgnoreFiles = Options.get("useIgnoreFiles").and_then(Value::as_bool).unwrap_or(true);
74						let FollowSymlinks = Options.get("followSymlinks").and_then(Value::as_bool).unwrap_or(false);
75						let Urls = run_time
76							.Environment
77							.FindFilesInWorkspace(Pattern, Exclude, MaxResults, UseIgnoreFiles, FollowSymlinks)
78							.await
79							.map_err(|Error| Error.to_string())?;
80						Ok(json!(Urls.into_iter().map(|U| U.to_string()).collect::<Vec<_>>()))
81					})
82				};
83
84			Some(Ok(Box::new(effect)))
85		},
86
87		"Search.TextSearch" => {
88			let effect =
89				move |run_time:Arc<ApplicationRunTime>| -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
90					Box::pin(async move {
91						let provider:Arc<dyn SearchProvider> = run_time.Environment.Require();
92						let query = Parameters.get(0).cloned().unwrap_or_default();
93						let options = Parameters.get(1).cloned().unwrap_or_default();
94						provider.TextSearch(query, options).await.map_err(|e| e.to_string())
95					})
96				};
97
98			Some(Ok(Box::new(effect)))
99		},
100
101		_ => None,
102	}
103}