Mountain/FileSystem/
FileExplorerViewProvider.rs

1// File: Mountain/Source/FileSystem/FileExplorerViewProvider.rs
2// Role: A native (Rust-implemented) TreeViewProvider for the file explorer
3// view. Responsibilities:
4//   - Provide the root-level items (workspace folders).
5//   - Provide the children for a given directory URI by reading the file
6//     system.
7//   - Construct `TreeItemDTO` JSON values for each file and directory.
8//
9// NOTE: This is a native provider, so it doesn't need to implement methods
10// called *by* extensions (like `RegisterTreeDataProvider`). It only implements
11// the "pull" methods called *by the host* (`GetChildren`, `GetTreeItem`).
12
13//! # File Explorer View Provider
14//!
15//! A native (Rust-implemented) TreeViewProvider that provides the data for
16//! the file explorer view.
17
18#![allow(non_snake_case, non_camel_case_types)]
19
20use std::sync::Arc;
21
22use Common::{
23	Effect::ApplicationRunTime::ApplicationRunTime,
24	Environment::Environment::Environment,
25	Error::CommonError::CommonError,
26	FileSystem::{DTO::FileTypeDTO::FileTypeDTO, ReadDirectory::ReadDirectory},
27	TreeView::TreeViewProvider::TreeViewProvider,
28};
29use async_trait::async_trait;
30use log::info;
31use serde_json::{Value, json};
32// Import AppHandle and Manager trait
33use tauri::{AppHandle, Manager};
34use url::Url;
35
36use crate::RunTime::ApplicationRunTime::ApplicationRunTime as MountainRunTime;
37
38#[derive(Clone)]
39pub struct FileExplorerViewProvider {
40	AppicationHandle:AppHandle,
41}
42
43impl Environment for FileExplorerViewProvider {}
44
45impl FileExplorerViewProvider {
46	pub fn New(AppicationHandle:AppHandle) -> Self { Self { AppicationHandle } }
47
48	// Helper function to create the DTO, merged with V2's format
49	fn CreateTreeItemDTO(&self, Name:&str, Uri:&Url, FileType:FileTypeDTO) -> Value {
50		json!({
51
52					"handle": Uri.to_string(),
53
54					"label": { "label": Name },
55
56		// 1: Collapsed, 0: None
57					"collapsibleState": if FileType == FileTypeDTO::Directory { 1 } else { 0 },
58
59					"resourceUri": json!({ "external": Uri.to_string() }),
60
61					"command": if FileType == FileTypeDTO::File {
62
63						Some(json!({
64
65							"id": "vscode.open",
66
67							"title": "Open File",
68
69							"arguments": [json!({ "external": Uri.to_string() })]
70						}))
71					} else {
72
73						None
74					}
75
76				})
77	}
78}
79
80#[async_trait]
81impl TreeViewProvider for FileExplorerViewProvider {
82	// --- PUSH methods (not used by native providers) ---
83	async fn RegisterTreeDataProvider(&self, _ViewIdentifier:String, _Options:Value) -> Result<(), CommonError> {
84		Ok(())
85	}
86
87	async fn UnregisterTreeDataProvider(&self, _ViewIdentifier:String) -> Result<(), CommonError> { Ok(()) }
88
89	async fn RevealTreeItem(
90		&self,
91
92		_ViewIdentifier:String,
93
94		_ItemHandle:String,
95
96		_Options:Value,
97	) -> Result<(), CommonError> {
98		Ok(())
99	}
100
101	async fn RefreshTreeView(&self, _ViewIdentifier:String, _ItemsToRefresh:Option<Value>) -> Result<(), CommonError> {
102		Ok(())
103	}
104
105	async fn SetTreeViewMessage(&self, _ViewIdentifier:String, _Message:Option<String>) -> Result<(), CommonError> {
106		Ok(())
107	}
108
109	async fn SetTreeViewTitle(
110		&self,
111
112		_ViewIdentifier:String,
113
114		_Title:Option<String>,
115
116		_Description:Option<String>,
117	) -> Result<(), CommonError> {
118		Ok(())
119	}
120
121	async fn SetTreeViewBadge(&self, _ViewIdentifier:String, _BadgeValue:Option<Value>) -> Result<(), CommonError> {
122		Ok(())
123	}
124
125	// --- PULL methods (implemented by native providers) ---
126
127	/// Retrieves the children for a given directory URI.
128	async fn GetChildren(
129		&self,
130
131		// Kept for trait signature compatibility, but unused.
132		_ViewIdentifier:String,
133
134		ElementHandle:Option<String>,
135	) -> Result<Vec<Value>, CommonError> {
136		let RunTime = self.AppicationHandle.state::<Arc<MountainRunTime>>().inner().clone();
137
138		let AppState = RunTime.Environment.ApplicationState.clone();
139
140		let PathToRead = if let Some(Handle) = ElementHandle {
141			// If an element is provided, it's a directory URI string.
142			Url::parse(&Handle)
143				.map_err(|_| {
144					CommonError::InvalidArgument {
145						ArgumentName:"ElementHandle".into(),
146
147						Reason:"Handle is not a valid URI".into(),
148					}
149				})?
150				.to_file_path()
151				.map_err(|_| {
152					CommonError::InvalidArgument {
153						ArgumentName:"ElementHandle".into(),
154
155						Reason:"Handle URI is not a file path".into(),
156					}
157				})?
158		} else {
159			// If no element, we are at the root. We should return the workspace folders.
160			let Folders = AppState.WorkSpaceFolders.lock().unwrap();
161
162			let RootItems:Vec<Value> = Folders
163				.iter()
164				.map(|folder| self.CreateTreeItemDTO(&folder.Name, &folder.URI, FileTypeDTO::Directory))
165				.collect();
166
167			return Ok(RootItems);
168		};
169
170		info!("[FileExplorer] Getting children for path: {}", PathToRead.display());
171
172		// This now works because `RunTime` has the correct type and implements the
173		// `ApplicationRunTime` trait.
174		let Entries = RunTime.Run(ReadDirectory(PathToRead.clone())).await?;
175
176		let Items = Entries
177			.into_iter()
178			.map(|(Name, FileType)| {
179				let FullPath = PathToRead.join(&Name);
180
181				let Uri = Url::from_file_path(FullPath).unwrap();
182
183				self.CreateTreeItemDTO(&Name, &Uri, FileType)
184			})
185			.collect();
186
187		Ok(Items)
188	}
189
190	/// Retrieves the `TreeItem` for a given handle (which is its URI).
191	async fn GetTreeItem(&self, _ViewIdentifier:String, ElementHandle:String) -> Result<Value, CommonError> {
192		let URI = Url::parse(&ElementHandle).map_err(|Error| {
193			CommonError::InvalidArgument { ArgumentName:"ElementHandle".into(), Reason:Error.to_string() }
194		})?;
195
196		let Name = URI.path_segments().and_then(|s| s.last()).unwrap_or("").to_string();
197
198		// Use robust check from V1
199		let IsDirectory = URI.as_str().ends_with('/') || URI.to_file_path().map_or(false, |p| p.is_dir());
200
201		let FileType = if IsDirectory { FileTypeDTO::Directory } else { FileTypeDTO::File };
202
203		Ok(self.CreateTreeItemDTO(&Name, &URI, FileType))
204	}
205}