1use std::{collections::HashMap, sync::Arc};
38
39use serde_json::{Value, json};
40use tauri::{AppHandle, Emitter};
41use tokio::sync::RwLock;
42use tonic::{Request, Response, Status};
43
44use crate::{
45 RunTime::ApplicationRunTime::ApplicationRunTime,
46 Track,
47 Vine::Generated::{
48 CancelOperationRequest,
49 Empty,
50 GenericNotification,
51 GenericRequest,
52 GenericResponse,
53 RpcError as RPCError,
54 mountain_service_server::MountainService,
55 },
56 dev_log,
57};
58
59#[allow(dead_code)]
61mod ServiceConfig {
62
63 pub const MAX_CONCURRENT_OPERATIONS:usize = 50;
65
66 pub const CANCELLATION_TIMEOUT_MS:u64 = 5000;
68
69 pub const MAX_METHOD_NAME_LENGTH:usize = 128;
71}
72
73pub struct MountainVinegRPCService {
79 ApplicationHandle:AppHandle,
81
82 RunTime:Arc<ApplicationRunTime>,
84
85 ActiveOperations:Arc<RwLock<HashMap<u64, tokio_util::sync::CancellationToken>>>,
88}
89
90impl MountainVinegRPCService {
91 pub fn ApplicationHandle(&self) -> &AppHandle { &self.ApplicationHandle }
97
98 pub fn RunTime(&self) -> &Arc<ApplicationRunTime> { &self.RunTime }
103}
104
105impl MountainVinegRPCService {
106 pub fn Create(ApplicationHandle:AppHandle, RunTime:Arc<ApplicationRunTime>) -> Self {
115 dev_log!("grpc", "[MountainVinegRPCService] New instance created");
116
117 Self {
118 ApplicationHandle,
119
120 RunTime,
121
122 ActiveOperations:Arc::new(RwLock::new(HashMap::new())),
123 }
124 }
125
126 pub async fn RegisterOperation(&self, request_id:u64) -> tokio_util::sync::CancellationToken {
134 let token = tokio_util::sync::CancellationToken::new();
135
136 self.ActiveOperations.write().await.insert(request_id, token.clone());
137
138 dev_log!(
139 "grpc",
140 "[MountainVinegRPCService] Registered operation {} for cancellation",
141 request_id
142 );
143
144 token
145 }
146
147 pub async fn UnregisterOperation(&self, request_id:u64) {
152 self.ActiveOperations.write().await.remove(&request_id);
153
154 dev_log!("grpc", "[MountainVinegRPCService] Unregistered operation {}", request_id);
155 }
156
157 fn ValidateRequest(&self, request:&GenericRequest) -> Result<(), Status> {
166 if request.method.is_empty() {
168 return Err(Status::invalid_argument("Method name cannot be empty"));
169 }
170
171 if request.method.len() > ServiceConfig::MAX_METHOD_NAME_LENGTH {
172 return Err(Status::invalid_argument(format!(
173 "Method name exceeds maximum length of {} characters",
174 ServiceConfig::MAX_METHOD_NAME_LENGTH
175 )));
176 }
177
178 if request.parameter.len() > 4 * 1024 * 1024 {
180 return Err(Status::resource_exhausted("Request parameter size exceeds limit"));
181 }
182
183 if request.method.contains("../") || request.method.contains("::") {
185 return Err(Status::permission_denied("Invalid method name format"));
186 }
187
188 Ok(())
189 }
190
191 fn CreateErrorResponse(RequestIdentifier:u64, code:i32, message:String, data:Option<Vec<u8>>) -> GenericResponse {
202 GenericResponse {
203 request_identifier:RequestIdentifier,
204
205 result:vec![],
206
207 error:Some(RPCError { code, message, data:data.unwrap_or_default() }),
208 }
209 }
210
211 fn CreateSuccessResponse(RequestIdentifier:u64, result:&Value) -> GenericResponse {
220 let result_bytes = match serde_json::to_vec(result) {
221 Ok(bytes) => bytes,
222
223 Err(e) => {
224 dev_log!("grpc", "error: [MountainVinegRPCService] Failed to serialize result: {}", e);
225
226 return Self::CreateErrorResponse(
228 RequestIdentifier,
229 -32603, "Failed to serialize response".to_string(),
231 None,
232 );
233 },
234 };
235
236 GenericResponse { request_identifier:RequestIdentifier, result:result_bytes, error:None }
237 }
238}
239
240#[tonic::async_trait]
241impl MountainService for MountainVinegRPCService {
242 type OpenChannelFromCocoonStream = std::pin::Pin<
248 Box<
249 dyn tonic::codegen::tokio_stream::Stream<Item = Result<crate::Vine::Generated::Envelope, tonic::Status>>
250 + Send
251 + 'static,
252 >,
253 >;
254
255 async fn open_channel_from_cocoon(
256 &self,
257
258 _request:tonic::Request<tonic::Streaming<crate::Vine::Generated::Envelope>>,
259 ) -> Result<tonic::Response<Self::OpenChannelFromCocoonStream>, tonic::Status> {
260 Err(tonic::Status::unimplemented(
261 "OpenChannelFromCocoon: streaming multiplexer not yet wired (Patch 14); use unary endpoints",
262 ))
263 }
264
265 async fn process_cocoon_request(
280 &self,
281
282 request:Request<GenericRequest>,
283 ) -> Result<Response<GenericResponse>, Status> {
284 let RequestData = request.into_inner();
285
286 let MethodName = RequestData.method.clone();
287
288 let RequestIdentifier = RequestData.request_identifier;
289
290 let ReceiveInstant = std::time::Instant::now();
291
292 dev_log!(
301 "grpc-verbose",
302 "[MountainVinegRPCService] recv id={} method={} size={}B",
303 RequestIdentifier,
304 MethodName,
305 RequestData.parameter.len()
306 );
307
308 let IsHotRpc = matches!(
317 MethodName.as_str(),
318 "$tree:register" | "tree.register" | "Configuration.Inspect" | "Command.Execute"
319 );
320
321 if IsHotRpc {
322 let InstrumentRecvNs = std::time::SystemTime::now()
323 .duration_since(std::time::UNIX_EPOCH)
324 .map(|D| D.as_nanos())
325 .unwrap_or(0);
326
327 dev_log!(
331 "rpc-latency",
332 "[LandFix:RPC] grpc-recv method={} id={} size={} t_ns={}",
333 MethodName,
334 RequestIdentifier,
335 RequestData.parameter.len(),
336 InstrumentRecvNs
337 );
338 }
339
340 if let Err(status) = self.ValidateRequest(&RequestData) {
342 dev_log!("grpc", "warn: [MountainVinegRPCService] Request validation failed: {}", status);
343
344 return Ok(Response::new(Self::CreateErrorResponse(
345 RequestIdentifier,
346 -32602, status.message().to_string(),
348 None,
349 )));
350 }
351
352 let ParametersValue:Value = match serde_json::from_slice(&RequestData.parameter) {
356 Ok(v) => v,
357
358 Err(e) => {
359 let msg = format!("Failed to deserialize parameters for method '{}': {}", MethodName, e);
360
361 dev_log!("grpc", "error: {}", msg);
362
363 return Ok(Response::new(Self::CreateErrorResponse(
364 RequestIdentifier,
365 -32700, msg,
367 None,
368 )));
369 },
370 };
371
372 let DispatchResult = Track::SideCarRequest::DispatchSideCarRequest::DispatchSideCarRequest(
378 self.ApplicationHandle.clone(),
379 self.RunTime.clone(),
380 "cocoon-main".to_string(),
382 MethodName.clone(),
383 ParametersValue,
384 )
385 .await;
386
387 match DispatchResult {
388 Ok(SuccessfulResult) => {
389 if IsHotRpc {
390 dev_log!(
394 "rpc-latency",
395 "[LandFix:RPC] dispatched method={} id={} elapsed={}ms",
396 MethodName,
397 RequestIdentifier,
398 ReceiveInstant.elapsed().as_millis()
399 );
400 }
401
402 dev_log!(
407 "grpc-verbose",
408 "[MountainVinegRPCService] Request [ID: {}] completed successfully",
409 RequestIdentifier
410 );
411
412 Ok(Response::new(Self::CreateSuccessResponse(RequestIdentifier, &SuccessfulResult)))
413 },
414
415 Err(ErrorString) => {
416 let LowerError = ErrorString.to_lowercase();
427
428 let LooksLike404 = (MethodName == "FileSystem.ReadFile"
440 || MethodName == "FileSystem.Stat"
441 || MethodName == "FileSystem.ReadDirectory")
442 && (LowerError.contains("resource not found")
443 || LowerError.contains("not found")
444 || LowerError.contains("enoent")
445 || LowerError.contains("no such file or directory")
446 || LowerError.contains("entity not found")
447 || LowerError.contains("os error 2")
448 || LowerError.contains("path is outside of the registered workspace")
449 || LowerError.contains("permission denied for operation")
450 || LowerError.contains("workspace is not trusted"));
451
452 if LooksLike404 {
453 dev_log!(
454 "grpc-verbose",
455 "[LandFix:MountainVinegRPC] Request [ID: {}] {} 404 (benign): {}",
456 RequestIdentifier,
457 MethodName,
458 ErrorString
459 );
460 } else {
461 dev_log!(
462 "grpc",
463 "error: [MountainVinegRPCService] Request [ID: {}] failed: {}",
464 RequestIdentifier,
465 ErrorString
466 );
467 }
468
469 let ErrorCode = if LooksLike404 { -32004 } else { -32000 };
473
474 Ok(Response::new(Self::CreateErrorResponse(
475 RequestIdentifier,
476 ErrorCode,
477 ErrorString,
478 None,
479 )))
480 },
481 }
482 }
483
484 async fn send_cocoon_notification(&self, request:Request<GenericNotification>) -> Result<Response<Empty>, Status> {
504 let NotificationData = request.into_inner();
505
506 let MethodName = NotificationData.method;
507
508 dev_log!(
512 "grpc-verbose",
513 "[MountainVinegRPCService] Received gRPC Notification: Method='{}'",
514 MethodName
515 );
516
517 if MethodName.is_empty() {
519 dev_log!(
520 "grpc",
521 "warn: [MountainVinegRPCService] Received notification with empty method name"
522 );
523
524 return Err(Status::invalid_argument("Method name cannot be empty"));
525 }
526
527 let Parameter:Value = if NotificationData.parameter.is_empty() {
538 Value::Null
539 } else {
540 serde_json::from_slice(&NotificationData.parameter).unwrap_or(Value::Null)
541 };
542
543 match MethodName.as_str() {
544
545 "extensionHostMessage" => {
550
551 super::Notification::ExtensionHostMessage::ExtensionHostMessage(self, &Parameter).await;
552 },
553
554 "ExtensionActivated" => {
555
556 super::Notification::ExtensionActivated::ExtensionActivated(self, &Parameter).await;
557 },
558
559 "ExtensionDeactivated" => {
560
561 super::Notification::ExtensionDeactivated::ExtensionDeactivated(self, &Parameter).await;
562 },
563
564 "WebviewReady" => {
565
566 super::Notification::WebviewReady::WebviewReady(self, &Parameter).await;
567 },
568
569 "progress.start" => {
570
571 super::Notification::ProgressStart::ProgressStart(self, &Parameter).await;
572 },
573
574 "progress.report" => {
575
576 super::Notification::ProgressReport::ProgressReport(self, &Parameter).await;
577 },
578
579 "progress.end" => {
580
581 super::Notification::ProgressEnd::ProgressEnd(self, &Parameter).await;
582 },
583
584 "languages.setDocumentLanguage" => {
585
586 super::Notification::LanguagesSetDocumentLanguage::LanguagesSetDocumentLanguage(self, &Parameter).await;
587 },
588
589 "workspace.applyEdit" => {
590
591 super::Notification::WorkspaceApplyEdit::WorkspaceApplyEdit(self, &Parameter).await;
592 },
593
594 "window.showTextDocument" => {
595
596 super::Notification::WindowShowTextDocument::WindowShowTextDocument(self, &Parameter).await;
597 },
598
599 "webview.setTitle"
605 | "webview.setIconPath"
606 | "webview.setHtml"
607 | "webview.updateView"
608 | "webview.reveal" => {
609
610 super::Notification::WebviewLifecycle::WebviewLifecycle(self, &MethodName, &Parameter).await;
611 },
612
613 "window.createTerminal" => {
614
615 super::Notification::WindowCreateTerminal::WindowCreateTerminal(self, &Parameter).await;
616 },
617
618 "terminal.sendText" | "terminal.show" | "terminal.hide" | "terminal.dispose" => {
619
620 super::Notification::TerminalLifecycle::TerminalLifecycle(self, &MethodName, &Parameter).await;
621 },
622
623 "window.createTextEditorDecorationType" | "window.disposeTextEditorDecorationType" => {
624
625 super::Notification::DecorationTypeLifecycle::DecorationTypeLifecycle(self, &MethodName, &Parameter).await;
626 },
627
628 "window.setTextEditorDecorations" => {
632
633 super::Notification::SetTextEditorDecorations::SetTextEditorDecorations(self, &Parameter).await;
634 },
635
636 "window.applyTextEdits" => {
640
641 super::Notification::ApplyTextEdits::ApplyTextEdits(self, &Parameter).await;
642 },
643
644 "debug.addBreakpoints" | "debug.removeBreakpoints" | "debug.consoleAppend" => {
645
646 super::Notification::DebugLifecycle::DebugLifecycle(self, &MethodName, &Parameter).await;
647 },
648
649 "statusBar.update" | "statusBar.dispose" => {
650
651 super::Notification::StatusBarLifecycle::StatusBarLifecycle(self, &MethodName, &Parameter).await;
652 },
653
654 "statusBar.message" => {
655
656 super::Notification::StatusBarMessage::StatusBarMessage(self, &Parameter).await;
657 },
658
659 "window.showMessage" => {
660
661 super::Notification::WindowShowMessage::WindowShowMessage(self, &Parameter).await;
662 },
663
664 "registerCommand" => {
665
666 super::Notification::RegisterCommand::RegisterCommand(self, &Parameter).await;
667 },
668
669 "unregisterCommand" => {
670
671 super::Notification::UnregisterCommand::UnregisterCommand(self, &Parameter).await;
672 },
673
674 "unregister_authentication_provider" => {
684 super::Notification::UnregisterAuthenticationProvider::UnregisterAuthenticationProvider(self, &Parameter).await;
685 },
686 "unregister_debug_adapter" => {
687 super::Notification::UnregisterDebugAdapter::UnregisterDebugAdapter(self, &Parameter).await;
688 },
689 "unregister_debug_configuration_provider" => {
690 super::Notification::UnregisterDebugConfigurationProvider::UnregisterDebugConfigurationProvider(
691 self,
692 &Parameter,
693 )
694 .await;
695 },
696 "unregister_file_system_provider" => {
697 super::Notification::UnregisterFileSystemProvider::UnregisterFileSystemProvider(self, &Parameter).await;
698 },
699 "unregister_scm_provider" => {
700 super::Notification::UnregisterScmProvider::UnregisterScmProvider(self, &Parameter).await;
701 },
702 "unregister_task_provider" => {
703 super::Notification::UnregisterTaskProvider::UnregisterTaskProvider(self, &Parameter).await;
704 },
705 "unregister_uri_handler" => {
706 super::Notification::UnregisterUriHandler::UnregisterUriHandler(self, &Parameter).await;
707 },
708 "update_scm_group" => {
709 super::Notification::UpdateScmGroup::UpdateScmGroup(self, &Parameter).await;
710 },
711 "register_scm_provider" => {
722 super::Notification::RegisterScmProvider::RegisterScmProvider(self, &Parameter).await;
723 },
724 "register_scm_resource_group" => {
725 super::Notification::RegisterScmResourceGroup::RegisterScmResourceGroup(self, &Parameter).await;
726 },
727
728 "progress.update" => {
730 super::Notification::ProgressUpdate::ProgressUpdate(self, &Parameter).await;
731 },
732 "progress.complete" => {
733 super::Notification::ProgressComplete::ProgressComplete(self, &Parameter).await;
734 },
735
736 "setStatusBarText" => {
738 super::Notification::SetStatusBarText::SetStatusBarText(self, &Parameter).await;
739 },
740 "disposeStatusBarItem" => {
741 super::Notification::DisposeStatusBarItem::DisposeStatusBarItem(self, &Parameter).await;
742 },
743
744 "output.create" => {
749 super::Notification::OutputCreate::OutputCreate(self, &Parameter).await;
750 },
751 "output.append" => {
752 super::Notification::OutputAppend::OutputAppend(self, &Parameter).await;
753 },
754 "output.appendLine" => {
755 super::Notification::OutputAppendLine::OutputAppendLine(self, &Parameter).await;
756 },
757 "output.clear" => {
758 super::Notification::OutputClear::OutputClear(self, &Parameter).await;
759 },
760 "output.show" => {
761 super::Notification::OutputShow::OutputShow(self, &Parameter).await;
762 },
763 "output.dispose" => {
764 super::Notification::OutputDispose::OutputDispose(self, &Parameter).await;
765 },
766 "output.replace" => {
767 super::Notification::OutputReplace::OutputReplace(self, &Parameter).await;
768 },
769 "outputChannel.create" => {
770 super::Notification::OutputChannelCreate::OutputChannelCreate(self, &Parameter).await;
771 },
772 "outputChannel.append" => {
773 super::Notification::OutputChannelAppend::OutputChannelAppend(self, &Parameter).await;
774 },
775 "outputChannel.clear" => {
776 super::Notification::OutputChannelClear::OutputChannelClear(self, &Parameter).await;
777 },
778 "outputChannel.show" => {
779 super::Notification::OutputChannelShow::OutputChannelShow(self, &Parameter).await;
780 },
781 "outputChannel.hide" => {
782 super::Notification::OutputChannelHide::OutputChannelHide(self, &Parameter).await;
783 },
784 "outputChannel.dispose" => {
785 super::Notification::OutputChannelDispose::OutputChannelDispose(self, &Parameter).await;
786 },
787
788 "webview.postMessage" => {
790 super::Notification::WebviewPostMessage::WebviewPostMessage(self, &Parameter).await;
791 },
792 "webview.dispose" => {
793 super::Notification::WebviewDispose::WebviewDispose(self, &Parameter).await;
794 },
795
796 "set_language_configuration" => {
798 super::Notification::SetLanguageConfiguration::SetLanguageConfiguration(self, &Parameter).await;
799 },
800 "openExternal" => {
801 super::Notification::OpenExternal::OpenExternal(self, &Parameter).await;
802 },
803 "security.incident" => {
804 super::Notification::SecurityIncident::SecurityIncident(self, &Parameter).await;
805 },
806
807 "register_authentication_provider"
829 | "register_call_hierarchy_provider"
830 | "register_code_actions_provider"
831 | "register_code_lens_provider"
832 | "register_color_provider"
833 | "register_completion_item_provider"
834 | "register_debug_adapter"
835 | "register_debug_configuration_provider"
836 | "register_declaration_provider"
837 | "register_definition_provider"
838 | "register_document_drop_edit_provider"
839 | "register_document_formatting_provider"
840 | "register_document_highlight_provider"
841 | "register_document_link_provider"
842 | "register_document_paste_edit_provider"
843 | "register_document_range_formatting_provider"
844 | "register_document_symbol_provider"
845 | "register_evaluatable_expression_provider"
846 | "register_external_uri_opener"
847 | "register_file_decoration_provider"
848 | "register_file_system_provider"
849 | "register_folding_range_provider"
850 | "register_hover_provider"
851 | "register_implementation_provider"
852 | "register_inlay_hints_provider"
853 | "register_inline_completion_item_provider"
854 | "register_inline_edit_provider"
855 | "register_inline_values_provider"
856 | "register_linked_editing_range_provider"
857 | "register_mapped_edits_provider"
858 | "register_multi_document_highlight_provider"
859 | "register_notebook_content_provider"
860 | "register_notebook_serializer"
861 | "register_on_type_formatting_provider"
862 | "register_reference_provider"
863 | "register_remote_authority_resolver"
864 | "register_rename_provider"
865 | "register_resource_label_formatter"
866 | "register_selection_range_provider"
873 | "register_semantic_tokens_provider"
874 | "register_signature_help_provider"
875 | "register_task_provider"
876 | "register_terminal_link_provider"
877 | "register_terminal_profile_provider"
878 | "register_text_document_content_provider"
879 | "register_type_definition_provider"
880 | "register_type_hierarchy_provider"
881 | "register_uri_handler"
882 | "register_workspace_symbol_provider" => {
883 let Handle = Parameter.get("handle").and_then(|h| h.as_u64()).unwrap_or(0) as u32;
884 let Selector = Parameter
891 .get("languageSelector")
892 .or_else(|| Parameter.get("language_selector"))
893 .and_then(|s| s.as_str())
894 .unwrap_or("*");
895 let ExtId = Parameter
896 .get("extensionId")
897 .or_else(|| Parameter.get("extension_id"))
898 .and_then(|e| e.as_str())
899 .unwrap_or("");
900 let Scheme = Parameter.get("scheme").and_then(|s| s.as_str()).unwrap_or("");
903 let ProviderTypeName = MethodName
904 .strip_prefix("register_")
905 .map(|Stripped| Stripped.strip_suffix("_provider").unwrap_or(Stripped))
906 .unwrap_or("");
907 dev_log!(
914 "grpc-verbose",
915 "[MountainVinegRPCService] Cocoon registered {} provider: handle={}, lang={}",
916 ProviderTypeName,
917 Handle,
918 Selector
919 );
920 dev_log!(
921 "provider-register",
922 "[ProviderRegister] accepted method={} type={} handle={} lang={} scheme={} ext={}",
923 MethodName,
924 ProviderTypeName,
925 Handle,
926 Selector,
927 Scheme,
928 ExtId
929 );
930 use CommonLibrary::LanguageFeature::DTO::ProviderType::ProviderType as PT;
931 let ProvType = match ProviderTypeName {
932 "authentication" => Some(PT::Authentication),
933 "call_hierarchy" => Some(PT::CallHierarchy),
934 "code_actions" => Some(PT::CodeAction),
935 "code_lens" => Some(PT::CodeLens),
936 "color" => Some(PT::Color),
937 "completion_item" => Some(PT::Completion),
938 "debug_adapter" => Some(PT::DebugAdapter),
939 "debug_configuration" => Some(PT::DebugConfiguration),
940 "declaration" => Some(PT::Declaration),
941 "definition" => Some(PT::Definition),
942 "document_drop_edit" => Some(PT::DocumentDropEdit),
943 "document_formatting" => Some(PT::DocumentFormatting),
944 "document_highlight" => Some(PT::DocumentHighlight),
945 "document_link" => Some(PT::DocumentLink),
946 "document_paste_edit" => Some(PT::DocumentPasteEdit),
947 "document_range_formatting" => Some(PT::DocumentRangeFormatting),
948 "document_symbol" => Some(PT::DocumentSymbol),
949 "evaluatable_expression" => Some(PT::EvaluatableExpression),
950 "external_uri_opener" => Some(PT::ExternalUriOpener),
951 "file_decoration" => Some(PT::FileDecoration),
952 "file_system" => Some(PT::FileSystem),
953 "folding_range" => Some(PT::FoldingRange),
954 "hover" => Some(PT::Hover),
955 "implementation" => Some(PT::Implementation),
956 "inlay_hints" => Some(PT::InlayHint),
957 "inline_completion_item" => Some(PT::InlineCompletion),
958 "inline_edit" => Some(PT::InlineEdit),
959 "inline_values" => Some(PT::InlineValues),
960 "linked_editing_range" => Some(PT::LinkedEditingRange),
961 "mapped_edits" => Some(PT::MappedEdits),
962 "multi_document_highlight" => Some(PT::MultiDocumentHighlight),
963 "notebook_content" => Some(PT::NotebookContent),
964 "notebook_serializer" => Some(PT::NotebookSerializer),
965 "on_type_formatting" => Some(PT::OnTypeFormatting),
966 "reference" => Some(PT::References),
967 "remote_authority_resolver" => Some(PT::RemoteAuthorityResolver),
968 "rename" => Some(PT::Rename),
969 "resource_label_formatter" => Some(PT::ResourceLabelFormatter),
970 "scm" => Some(PT::SourceControl),
971 "scm_resource_group" => Some(PT::ScmResourceGroup),
972 "selection_range" => Some(PT::SelectionRange),
973 "semantic_tokens" => Some(PT::SemanticTokens),
974 "signature_help" => Some(PT::SignatureHelp),
975 "task" => Some(PT::Task),
976 "terminal_link" => Some(PT::TerminalLink),
977 "terminal_profile" => Some(PT::TerminalProfile),
978 "text_document_content" => Some(PT::TextDocumentContent),
979 "type_definition" => Some(PT::TypeDefinition),
980 "type_hierarchy" => Some(PT::TypeHierarchy),
981 "uri_handler" => Some(PT::UriHandler),
982 "workspace_symbol" => Some(PT::WorkspaceSymbol),
983 _ => None,
984 };
985 if let Some(ProviderType) = ProvType {
986 use crate::ApplicationState::DTO::ProviderRegistrationDTO::ProviderRegistrationDTO;
987 let SelectorValue = if !Scheme.is_empty() {
991 json!([{ "scheme": Scheme, "language": Selector }])
992 } else {
993 json!([{ "language": Selector }])
994 };
995 let Dto = ProviderRegistrationDTO {
996 Handle,
997 ProviderType,
998 Selector:SelectorValue,
999 SideCarIdentifier:"cocoon-main".to_string(),
1000 ExtensionIdentifier:json!(ExtId),
1001 Options:Parameter.get("options").cloned(),
1002 };
1003 self.RunTime
1004 .Environment
1005 .ApplicationState
1006 .Extension
1007 .ProviderRegistration
1008 .RegisterProvider(Handle, Dto);
1009 }
1010 },
1011 _ => {
1012 dev_log!("grpc", "[MountainVinegRPCService] Cocoon notification: {}", MethodName);
1013 let PayloadPreview = if NotificationData.parameter.len() <= 160 {
1026 String::from_utf8_lossy(&NotificationData.parameter).into_owned()
1027 } else {
1028 let Slice = &NotificationData.parameter[..160];
1029 format!("{}…", String::from_utf8_lossy(Slice))
1030 };
1031 dev_log!(
1032 "notif-drop",
1033 "[NotifDrop] method={} payload_bytes={} preview={:?} (falls through to cocoon:{} event)",
1034 MethodName,
1035 NotificationData.parameter.len(),
1036 PayloadPreview,
1037 MethodName
1038 );
1039 let EventName = format!("cocoon:{}", MethodName);
1042 if let Err(Error) = self.ApplicationHandle.emit(&EventName, &Parameter) {
1043 dev_log!(
1044 "grpc",
1045 "warn: [MountainVinegRPCService] Failed to emit {}: {}",
1046 EventName,
1047 Error
1048 );
1049 }
1050 },
1051 }
1052
1053 Ok(Response::new(Empty {}))
1054 }
1055
1056 async fn cancel_operation(&self, request:Request<CancelOperationRequest>) -> Result<Response<Empty>, Status> {
1068 let cancel_request = request.into_inner();
1069
1070 let RequestIdentifierToCancel = cancel_request.request_identifier_to_cancel;
1071
1072 dev_log!(
1073 "grpc",
1074 "[MountainVinegRPCService] Received CancelOperation request for RequestID: {}",
1075 RequestIdentifierToCancel
1076 );
1077
1078 let cancel_token = {
1080 let operations = self.ActiveOperations.read().await;
1081 operations.get(&RequestIdentifierToCancel).cloned()
1082 };
1083
1084 match cancel_token {
1085 Some(token) => {
1086 token.cancel();
1088
1089 dev_log!(
1090 "grpc",
1091 "[MountainVinegRPCService] Successfully initiated cancellation for operation {}",
1092 RequestIdentifierToCancel
1093 );
1094
1095 Ok(Response::new(Empty {}))
1100 },
1101 None => {
1102 dev_log!(
1104 "grpc",
1105 "warn: [MountainVinegRPCService] Cannot cancel operation {}: operation not found (may have \
1106 already completed)",
1107 RequestIdentifierToCancel
1108 );
1109
1110 Ok(Response::new(Empty {}))
1112 },
1113 }
1114 }
1115}