Skip to main content

DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/FileSystem/
FileExplorerViewProvider.rs

1//! Native TreeView provider for the workspace file explorer. Implements
2//! `CommonLibrary::TreeView::TreeViewProvider`.
3//!
4//! Pull-only: `GetChildren` reads the workspace folders (when `ElementHandle`
5//! is `None`) or the directory the handle points to. `GetTreeItem` builds a
6//! single VS Code-shaped `TreeItemDTO`. Push methods are no-ops because the
7//! provider is read-only and registered directly in `ApplicationState`.
8
9use std::sync::Arc;
10
11use CommonLibrary::{
12	Effect::ApplicationRunTime::ApplicationRunTime as _,
13	Environment::Environment::Environment,
14	Error::CommonError::CommonError,
15	FileSystem::{DTO::FileTypeDTO::FileTypeDTO, ReadDirectory::ReadDirectory},
16	TreeView::TreeViewProvider::TreeViewProvider,
17};
18use async_trait::async_trait;
19use serde_json::{Value, json};
20use tauri::{AppHandle, Manager};
21use url::Url;
22
23use crate::{RunTime::ApplicationRunTime::ApplicationRunTime as Runtime, dev_log};
24
25#[derive(Clone)]
26pub struct Struct {
27	AppicationHandle:AppHandle,
28}
29
30impl Environment for Struct {}
31
32impl Struct {
33	pub fn New(AppicationHandle:AppHandle) -> Self { Self { AppicationHandle } }
34
35	fn CreateTreeItemDTO(&self, Name:&str, URI:&Url, FileType:FileTypeDTO) -> Value {
36		json!({
37			"handle": URI.to_string(),
38			"label": { "label": Name },
39			// 1 = collapsed, 0 = leaf.
40			"collapsibleState": if FileType == FileTypeDTO::Directory { 1 } else { 0 },
41			"resourceUri": json!({ "external": URI.to_string() }),
42			"command": if FileType == FileTypeDTO::File {
43				Some(json!({
44					"id": "vscode.open",
45					"title": "Open File",
46					"arguments": [json!({ "external": URI.to_string() })]
47				}))
48			} else {
49				None
50			}
51		})
52	}
53}
54
55#[async_trait]
56impl TreeViewProvider for Struct {
57	// Push methods - no-ops for native providers.
58
59	async fn RegisterTreeDataProvider(&self, _ViewIdentifier:String, _Options:Value) -> Result<(), CommonError> {
60		Ok(())
61	}
62
63	async fn UnregisterTreeDataProvider(&self, _ViewIdentifier:String) -> Result<(), CommonError> { Ok(()) }
64
65	async fn RevealTreeItem(
66		&self,
67
68		_ViewIdentifier:String,
69
70		_ItemHandle:String,
71
72		_Options:Value,
73	) -> Result<(), CommonError> {
74		Ok(())
75	}
76
77	async fn RefreshTreeView(&self, _ViewIdentifier:String, _ItemsToRefresh:Option<Value>) -> Result<(), CommonError> {
78		Ok(())
79	}
80
81	async fn SetTreeViewMessage(&self, _ViewIdentifier:String, _Message:Option<String>) -> Result<(), CommonError> {
82		Ok(())
83	}
84
85	async fn SetTreeViewTitle(
86		&self,
87
88		_ViewIdentifier:String,
89
90		_Title:Option<String>,
91
92		_Description:Option<String>,
93	) -> Result<(), CommonError> {
94		Ok(())
95	}
96
97	async fn SetTreeViewBadge(&self, _ViewIdentifier:String, _BadgeValue:Option<Value>) -> Result<(), CommonError> {
98		Ok(())
99	}
100
101	async fn OnTreeNodeExpanded(
102		&self,
103
104		_ViewIdentifier:String,
105
106		_ElementHandle:String,
107
108		_IsExpanded:bool,
109	) -> Result<(), CommonError> {
110		dev_log!("vfs", "[FileExplorer] OnTreeNodeExpanded - native provider no-op");
111
112		Ok(())
113	}
114
115	async fn OnTreeSelectionChanged(
116		&self,
117
118		_ViewIdentifier:String,
119
120		_SelectedHandles:Vec<String>,
121	) -> Result<(), CommonError> {
122		dev_log!("vfs", "[FileExplorer] OnTreeSelectionChanged - native provider no-op");
123
124		Ok(())
125	}
126
127	async fn PersistTreeViewState(&self, _ViewIdentifier:String) -> Result<Value, CommonError> {
128		Ok(json!({ "supported": false }))
129	}
130
131	async fn RestoreTreeViewState(&self, _ViewIdentifier:String, _StateValue:Value) -> Result<(), CommonError> {
132		Ok(())
133	}
134
135	// Pull methods.
136
137	async fn GetChildren(
138		&self,
139
140		_ViewIdentifier:String,
141
142		ElementHandle:Option<String>,
143	) -> Result<Vec<Value>, CommonError> {
144		let RunTime = self.AppicationHandle.state::<Arc<Runtime>>().inner().clone();
145
146		let AppState = RunTime.Environment.ApplicationState.clone();
147
148		let PathToRead = if let Some(Handle) = ElementHandle {
149			Url::parse(&Handle)
150				.map_err(|_| {
151					CommonError::InvalidArgument {
152						ArgumentName:"ElementHandle".into(),
153						Reason:"Handle is not a valid URI".into(),
154					}
155				})?
156				.to_file_path()
157				.map_err(|_| {
158					CommonError::InvalidArgument {
159						ArgumentName:"ElementHandle".into(),
160						Reason:"Handle URI is not a file path".into(),
161					}
162				})?
163		} else {
164			let Folders = AppState.Workspace.WorkspaceFolders.lock().unwrap();
165
166			let RootItems:Vec<Value> = Folders
167				.iter()
168				.map(|Folder| self.CreateTreeItemDTO(&Folder.Name, &Folder.URI, FileTypeDTO::Directory))
169				.collect();
170
171			return Ok(RootItems);
172		};
173
174		dev_log!("vfs", "[FileExplorer] GetChildren {}", PathToRead.display());
175
176		let Entries:Vec<(String, FileTypeDTO)> = RunTime.Run(ReadDirectory(PathToRead.clone())).await?;
177
178		Ok(Entries
179			.into_iter()
180			.map(|(Name, FileType)| {
181				let FullPath = PathToRead.join(&Name);
182				let URI = Url::from_file_path(FullPath).unwrap();
183				self.CreateTreeItemDTO(&Name, &URI, FileType)
184			})
185			.collect())
186	}
187
188	async fn GetTreeItem(&self, _ViewIdentifier:String, ElementHandle:String) -> Result<Value, CommonError> {
189		let URI = Url::parse(&ElementHandle).map_err(|Error| {
190			CommonError::InvalidArgument { ArgumentName:"ElementHandle".into(), Reason:Error.to_string() }
191		})?;
192
193		let Name = URI.path_segments().and_then(|S| S.last()).unwrap_or("").to_string();
194
195		let IsDirectory = URI.as_str().ends_with('/') || URI.to_file_path().map_or(false, |P| P.is_dir());
196
197		let FileType = if IsDirectory { FileTypeDTO::Directory } else { FileTypeDTO::File };
198
199		Ok(self.CreateTreeItemDTO(&Name, &URI, FileType))
200	}
201}