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 serde_json::{Value, json};
16use tauri::{Emitter, Manager};
17use url::Url;
18
19use crate::{
20 ApplicationState::DTO::DocumentStateDTO::DocumentStateDTO,
21 Environment::Utility,
22 RunTime::ApplicationRunTime::ApplicationRunTime,
23 dev_log,
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 dev_log!("model", "[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 dev_log!("model", "[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 dev_log!(
55 "model",
56 "error: [DocumentProvider] Failed to emit document open event: {}",
57 error
58 );
59 }
60 },
61 Err(error) => {
62 dev_log!(
63 "model",
64 "error: [DocumentProvider] Failed to serialize existing document DTO: {}",
65 error
66 );
67 },
68 }
69
70 return Ok(existing_document.URI.clone());
71 }
72
73 let file_content = if let Some(c) = content {
75 c
76 } else if uri.scheme() == "file" {
77 let file_path = uri.to_file_path().map_err(|_| {
78 CommonError::InvalidArgument {
79 ArgumentName:"URI".into(),
80 Reason:"Cannot convert non-file URI to path".into(),
81 }
82 })?;
83
84 let runtime = environment.ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
85
86 let file_content_bytes = runtime.Run(ReadFile(file_path.clone())).await?;
87
88 String::from_utf8(file_content_bytes)
89 .map_err(|error| CommonError::FileSystemIO { Path:file_path, Description:error.to_string() })?
90 } else {
91 dev_log!(
93 "model",
94 "[DocumentProvider] Non-native scheme '{}'. Attempting to resolve from sidecar.",
95 uri.scheme()
96 );
97
98 let ipc_provider:Arc<dyn IPCProvider> = environment.Require();
99
100 let rpc_result = ipc_provider
101 .SendRequestToSideCar(
102 "cocoon-main".to_string(),
104 "$provideTextDocumentContent".to_string(),
105 json!([uri_components_dto]),
106 10000,
107 )
108 .await?;
109
110 rpc_result.as_str().map(String::from).ok_or_else(|| {
111 CommonError::IPCError {
112 Description:format!("Failed to get valid string content for custom URI scheme '{}'", uri.scheme()),
113 }
114 })?
115 };
116
117 let new_document = DocumentStateDTO::Create(uri.clone(), language_identifier, file_content)?;
119
120 let dto_for_notification = new_document.ToDTO()?;
121
122 environment
123 .ApplicationState
124 .Feature
125 .Documents
126 .OpenDocuments
127 .lock()
128 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?
129 .insert(uri.to_string(), new_document);
130
131 if let Err(error) = environment
132 .ApplicationHandle
133 .emit("sky://documents/open", dto_for_notification.clone())
134 {
135 dev_log!(
136 "model",
137 "error: [DocumentProvider] Failed to emit document open event: {}",
138 error
139 );
140 }
141
142 crate::Environment::DocumentProvider::Notifications::notify_model_added(environment, &dto_for_notification).await;
143
144 Ok(uri)
145}