Mountain/Environment/DocumentProvider/
OpenDocument.rs1use std::sync::Arc;
7
8use CommonLibrary::{
9 Effect::ApplicationRunTime::ApplicationRunTime as _,
10 Environment::Requires::Requires,
11 Error::CommonError::CommonError,
12 FileSystem::ReadFile::ReadFile,
13 IPC::IPCProvider::IPCProvider,
14};
15use log::{error, info};
16use serde_json::{Value, json};
17use tauri::{Emitter, Manager};
18use url::Url;
19
20use crate::{
21 ApplicationState::DTO::DocumentStateDTO::DocumentStateDTO,
22 Environment::Utility,
23 RunTime::ApplicationRunTime::ApplicationRunTime,
24};
25
26pub(super) async fn open_document(
30 environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
31 uri_components_dto:Value,
32 language_identifier:Option<String>,
33 content:Option<String>,
34) -> Result<Url, CommonError> {
35 let uri = Utility::GetURLFromURIComponentsDTO(&uri_components_dto)?;
36
37 info!("[DocumentProvider] Opening document: {}", uri);
38
39 if let Some(existing_document) = environment
41 .ApplicationState
42 .Feature
43 .Documents
44 .OpenDocuments
45 .lock()
46 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?
47 .get(uri.as_str())
48 {
49 info!("[DocumentProvider] Document {} is already open.", uri);
50
51 match existing_document.ToDTO() {
52 Ok(dto) => {
53 if let Err(error) = environment.ApplicationHandle.emit("sky://documents/open", dto) {
54 error!("[DocumentProvider] Failed to emit document open event: {}", error);
55 }
56 },
57 Err(error) => {
58 error!("[DocumentProvider] Failed to serialize existing document DTO: {}", error);
59 },
60 }
61
62 return Ok(existing_document.URI.clone());
63 }
64
65 let file_content = if let Some(c) = content {
67 c
68 } else if uri.scheme() == "file" {
69 let file_path = uri.to_file_path().map_err(|_| {
70 CommonError::InvalidArgument {
71 ArgumentName:"URI".into(),
72 Reason:"Cannot convert non-file URI to path".into(),
73 }
74 })?;
75
76 let runtime = environment.ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
77
78 let file_content_bytes = runtime.Run(ReadFile(file_path.clone())).await?;
79
80 String::from_utf8(file_content_bytes)
81 .map_err(|error| CommonError::FileSystemIO { Path:file_path, Description:error.to_string() })?
82 } else {
83 info!(
85 "[DocumentProvider] Non-native scheme '{}'. Attempting to resolve from sidecar.",
86 uri.scheme()
87 );
88
89 let ipc_provider:Arc<dyn IPCProvider> = environment.Require();
90
91 let rpc_result = ipc_provider
92 .SendRequestToSideCar(
93 "cocoon-main".to_string(),
95 "$provideTextDocumentContent".to_string(),
96 json!([uri_components_dto]),
97 10000,
98 )
99 .await?;
100
101 rpc_result.as_str().map(String::from).ok_or_else(|| {
102 CommonError::IPCError {
103 Description:format!("Failed to get valid string content for custom URI scheme '{}'", uri.scheme()),
104 }
105 })?
106 };
107
108 let new_document = DocumentStateDTO::Create(uri.clone(), language_identifier, file_content)?;
110
111 let dto_for_notification = new_document.ToDTO()?;
112
113 environment
114 .ApplicationState
115 .Feature
116 .Documents
117 .OpenDocuments
118 .lock()
119 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?
120 .insert(uri.to_string(), new_document);
121
122 if let Err(error) = environment
123 .ApplicationHandle
124 .emit("sky://documents/open", dto_for_notification.clone())
125 {
126 error!("[DocumentProvider] Failed to emit document open event: {}", error);
127 }
128
129 crate::Environment::DocumentProvider::Notifications::notify_model_added(environment, &dto_for_notification).await;
130
131 Ok(uri)
132}