Skip to main content

Mountain/Track/Effect/CreateEffectForRequest/
Search.rs

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