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!(
297 "grpc-verbose",
298 "[MountainVinegRPCService] Received gRPC Request [ID: {}]: Method='{}'",
299 RequestIdentifier,
300 MethodName
301 );
302
303 let IsHotRpc = matches!(
312 MethodName.as_str(),
313 "$tree:register" | "tree.register" | "Configuration.Inspect" | "Command.Execute"
314 );
315
316 if IsHotRpc {
317 let InstrumentRecvNs = std::time::SystemTime::now()
318 .duration_since(std::time::UNIX_EPOCH)
319 .map(|D| D.as_nanos())
320 .unwrap_or(0);
321
322 dev_log!(
326 "rpc-latency",
327 "[LandFix:RPC] grpc-recv method={} id={} size={} t_ns={}",
328 MethodName,
329 RequestIdentifier,
330 RequestData.parameter.len(),
331 InstrumentRecvNs
332 );
333 }
334
335 if let Err(status) = self.ValidateRequest(&RequestData) {
337 dev_log!("grpc", "warn: [MountainVinegRPCService] Request validation failed: {}", status);
338
339 return Ok(Response::new(Self::CreateErrorResponse(
340 RequestIdentifier,
341 -32602, status.message().to_string(),
343 None,
344 )));
345 }
346
347 let ParametersValue:Value = match serde_json::from_slice(&RequestData.parameter) {
349 Ok(v) => {
350 dev_log!(
363 "grpc-verbose",
364 "[MountainVinegRPCService] Params for [ID: {}] ({} bytes)",
365 RequestIdentifier,
366 RequestData.parameter.len()
367 );
368
369 v
370 },
371
372 Err(e) => {
373 let msg = format!("Failed to deserialize parameters for method '{}': {}", MethodName, e);
374
375 dev_log!("grpc", "error: {}", msg);
376
377 return Ok(Response::new(Self::CreateErrorResponse(
378 RequestIdentifier,
379 -32700, msg,
381 None,
382 )));
383 },
384 };
385
386 dev_log!(
387 "grpc-verbose",
388 "[MountainVinegRPCService] Dispatching request [ID: {}] to Track::DispatchLogic",
389 RequestIdentifier
390 );
391
392 let DispatchResult = Track::SideCarRequest::DispatchSideCarRequest::DispatchSideCarRequest(
394 self.ApplicationHandle.clone(),
395 self.RunTime.clone(),
396 "cocoon-main".to_string(),
398 MethodName.clone(),
399 ParametersValue,
400 )
401 .await;
402
403 match DispatchResult {
404 Ok(SuccessfulResult) => {
405 if IsHotRpc {
406 dev_log!(
410 "rpc-latency",
411 "[LandFix:RPC] dispatched method={} id={} elapsed={}ms",
412 MethodName,
413 RequestIdentifier,
414 ReceiveInstant.elapsed().as_millis()
415 );
416 }
417
418 dev_log!(
423 "grpc-verbose",
424 "[MountainVinegRPCService] Request [ID: {}] completed successfully",
425 RequestIdentifier
426 );
427
428 Ok(Response::new(Self::CreateSuccessResponse(RequestIdentifier, &SuccessfulResult)))
429 },
430
431 Err(ErrorString) => {
432 let LowerError = ErrorString.to_lowercase();
443
444 let LooksLike404 = (MethodName == "FileSystem.ReadFile"
456 || MethodName == "FileSystem.Stat"
457 || MethodName == "FileSystem.ReadDirectory")
458 && (LowerError.contains("resource not found")
459 || LowerError.contains("not found")
460 || LowerError.contains("enoent")
461 || LowerError.contains("no such file or directory")
462 || LowerError.contains("entity not found")
463 || LowerError.contains("os error 2")
464 || LowerError.contains("path is outside of the registered workspace")
465 || LowerError.contains("permission denied for operation")
466 || LowerError.contains("workspace is not trusted"));
467
468 if LooksLike404 {
469 dev_log!(
470 "grpc-verbose",
471 "[LandFix:MountainVinegRPC] Request [ID: {}] {} 404 (benign): {}",
472 RequestIdentifier,
473 MethodName,
474 ErrorString
475 );
476 } else {
477 dev_log!(
478 "grpc",
479 "error: [MountainVinegRPCService] Request [ID: {}] failed: {}",
480 RequestIdentifier,
481 ErrorString
482 );
483 }
484
485 let ErrorCode = if LooksLike404 { -32004 } else { -32000 };
489
490 Ok(Response::new(Self::CreateErrorResponse(
491 RequestIdentifier,
492 ErrorCode,
493 ErrorString,
494 None,
495 )))
496 },
497 }
498 }
499
500 async fn send_cocoon_notification(&self, request:Request<GenericNotification>) -> Result<Response<Empty>, Status> {
520 let NotificationData = request.into_inner();
521
522 let MethodName = NotificationData.method;
523
524 dev_log!(
528 "grpc-verbose",
529 "[MountainVinegRPCService] Received gRPC Notification: Method='{}'",
530 MethodName
531 );
532
533 if MethodName.is_empty() {
535 dev_log!(
536 "grpc",
537 "warn: [MountainVinegRPCService] Received notification with empty method name"
538 );
539
540 return Err(Status::invalid_argument("Method name cannot be empty"));
541 }
542
543 let Parameter:Value = if NotificationData.parameter.is_empty() {
554 Value::Null
555 } else {
556 serde_json::from_slice(&NotificationData.parameter).unwrap_or(Value::Null)
557 };
558
559 match MethodName.as_str() {
560
561 "extensionHostMessage" => {
566
567 super::Notification::ExtensionHostMessage::ExtensionHostMessage(self, &Parameter).await;
568 },
569
570 "ExtensionActivated" => {
571
572 super::Notification::ExtensionActivated::ExtensionActivated(self, &Parameter).await;
573 },
574
575 "ExtensionDeactivated" => {
576
577 super::Notification::ExtensionDeactivated::ExtensionDeactivated(self, &Parameter).await;
578 },
579
580 "WebviewReady" => {
581
582 super::Notification::WebviewReady::WebviewReady(self, &Parameter).await;
583 },
584
585 "progress.start" => {
586
587 super::Notification::ProgressStart::ProgressStart(self, &Parameter).await;
588 },
589
590 "progress.report" => {
591
592 super::Notification::ProgressReport::ProgressReport(self, &Parameter).await;
593 },
594
595 "progress.end" => {
596
597 super::Notification::ProgressEnd::ProgressEnd(self, &Parameter).await;
598 },
599
600 "languages.setDocumentLanguage" => {
601
602 super::Notification::LanguagesSetDocumentLanguage::LanguagesSetDocumentLanguage(self, &Parameter).await;
603 },
604
605 "workspace.applyEdit" => {
606
607 super::Notification::WorkspaceApplyEdit::WorkspaceApplyEdit(self, &Parameter).await;
608 },
609
610 "window.showTextDocument" => {
611
612 super::Notification::WindowShowTextDocument::WindowShowTextDocument(self, &Parameter).await;
613 },
614
615 "webview.setTitle"
621 | "webview.setIconPath"
622 | "webview.setHtml"
623 | "webview.updateView"
624 | "webview.reveal" => {
625
626 super::Notification::WebviewLifecycle::WebviewLifecycle(self, &MethodName, &Parameter).await;
627 },
628
629 "window.createTerminal" => {
630
631 super::Notification::WindowCreateTerminal::WindowCreateTerminal(self, &Parameter).await;
632 },
633
634 "terminal.sendText" | "terminal.show" | "terminal.hide" | "terminal.dispose" => {
635
636 super::Notification::TerminalLifecycle::TerminalLifecycle(self, &MethodName, &Parameter).await;
637 },
638
639 "window.createTextEditorDecorationType" | "window.disposeTextEditorDecorationType" => {
640
641 super::Notification::DecorationTypeLifecycle::DecorationTypeLifecycle(self, &MethodName, &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_file_system_provider" => {
690 super::Notification::UnregisterFileSystemProvider::UnregisterFileSystemProvider(self, &Parameter).await;
691 },
692 "unregister_scm_provider" => {
693 super::Notification::UnregisterScmProvider::UnregisterScmProvider(self, &Parameter).await;
694 },
695 "unregister_task_provider" => {
696 super::Notification::UnregisterTaskProvider::UnregisterTaskProvider(self, &Parameter).await;
697 },
698 "unregister_uri_handler" => {
699 super::Notification::UnregisterUriHandler::UnregisterUriHandler(self, &Parameter).await;
700 },
701 "update_scm_group" => {
702 super::Notification::UpdateScmGroup::UpdateScmGroup(self, &Parameter).await;
703 },
704 "register_scm_provider" => {
715 super::Notification::RegisterScmProvider::RegisterScmProvider(self, &Parameter).await;
716 },
717 "register_scm_resource_group" => {
718 super::Notification::RegisterScmResourceGroup::RegisterScmResourceGroup(self, &Parameter).await;
719 },
720
721 "progress.update" => {
723 super::Notification::ProgressUpdate::ProgressUpdate(self, &Parameter).await;
724 },
725 "progress.complete" => {
726 super::Notification::ProgressComplete::ProgressComplete(self, &Parameter).await;
727 },
728
729 "setStatusBarText" => {
731 super::Notification::SetStatusBarText::SetStatusBarText(self, &Parameter).await;
732 },
733 "disposeStatusBarItem" => {
734 super::Notification::DisposeStatusBarItem::DisposeStatusBarItem(self, &Parameter).await;
735 },
736
737 "output.create" => {
742 super::Notification::OutputCreate::OutputCreate(self, &Parameter).await;
743 },
744 "output.append" => {
745 super::Notification::OutputAppend::OutputAppend(self, &Parameter).await;
746 },
747 "output.appendLine" => {
748 super::Notification::OutputAppendLine::OutputAppendLine(self, &Parameter).await;
749 },
750 "output.clear" => {
751 super::Notification::OutputClear::OutputClear(self, &Parameter).await;
752 },
753 "output.show" => {
754 super::Notification::OutputShow::OutputShow(self, &Parameter).await;
755 },
756 "output.dispose" => {
757 super::Notification::OutputDispose::OutputDispose(self, &Parameter).await;
758 },
759 "output.replace" => {
760 super::Notification::OutputReplace::OutputReplace(self, &Parameter).await;
761 },
762 "outputChannel.create" => {
763 super::Notification::OutputChannelCreate::OutputChannelCreate(self, &Parameter).await;
764 },
765 "outputChannel.append" => {
766 super::Notification::OutputChannelAppend::OutputChannelAppend(self, &Parameter).await;
767 },
768 "outputChannel.clear" => {
769 super::Notification::OutputChannelClear::OutputChannelClear(self, &Parameter).await;
770 },
771 "outputChannel.show" => {
772 super::Notification::OutputChannelShow::OutputChannelShow(self, &Parameter).await;
773 },
774 "outputChannel.hide" => {
775 super::Notification::OutputChannelHide::OutputChannelHide(self, &Parameter).await;
776 },
777 "outputChannel.dispose" => {
778 super::Notification::OutputChannelDispose::OutputChannelDispose(self, &Parameter).await;
779 },
780
781 "webview.postMessage" => {
783 super::Notification::WebviewPostMessage::WebviewPostMessage(self, &Parameter).await;
784 },
785 "webview.dispose" => {
786 super::Notification::WebviewDispose::WebviewDispose(self, &Parameter).await;
787 },
788
789 "set_language_configuration" => {
791 super::Notification::SetLanguageConfiguration::SetLanguageConfiguration(self, &Parameter).await;
792 },
793 "openExternal" => {
794 super::Notification::OpenExternal::OpenExternal(self, &Parameter).await;
795 },
796 "security.incident" => {
797 super::Notification::SecurityIncident::SecurityIncident(self, &Parameter).await;
798 },
799
800 "register_authentication_provider"
822 | "register_call_hierarchy_provider"
823 | "register_code_actions_provider"
824 | "register_code_lens_provider"
825 | "register_color_provider"
826 | "register_completion_item_provider"
827 | "register_debug_adapter"
828 | "register_debug_configuration_provider"
829 | "register_declaration_provider"
830 | "register_definition_provider"
831 | "register_document_drop_edit_provider"
832 | "register_document_formatting_provider"
833 | "register_document_highlight_provider"
834 | "register_document_link_provider"
835 | "register_document_paste_edit_provider"
836 | "register_document_range_formatting_provider"
837 | "register_document_symbol_provider"
838 | "register_evaluatable_expression_provider"
839 | "register_external_uri_opener"
840 | "register_file_decoration_provider"
841 | "register_file_system_provider"
842 | "register_folding_range_provider"
843 | "register_hover_provider"
844 | "register_implementation_provider"
845 | "register_inlay_hints_provider"
846 | "register_inline_completion_item_provider"
847 | "register_inline_edit_provider"
848 | "register_inline_values_provider"
849 | "register_linked_editing_range_provider"
850 | "register_mapped_edits_provider"
851 | "register_multi_document_highlight_provider"
852 | "register_notebook_content_provider"
853 | "register_notebook_serializer"
854 | "register_on_type_formatting_provider"
855 | "register_reference_provider"
856 | "register_remote_authority_resolver"
857 | "register_rename_provider"
858 | "register_resource_label_formatter"
859 | "register_selection_range_provider"
866 | "register_semantic_tokens_provider"
867 | "register_signature_help_provider"
868 | "register_task_provider"
869 | "register_terminal_link_provider"
870 | "register_terminal_profile_provider"
871 | "register_text_document_content_provider"
872 | "register_type_definition_provider"
873 | "register_type_hierarchy_provider"
874 | "register_uri_handler"
875 | "register_workspace_symbol_provider" => {
876 let Handle = Parameter.get("handle").and_then(|h| h.as_u64()).unwrap_or(0) as u32;
877 let Selector = Parameter
884 .get("languageSelector")
885 .or_else(|| Parameter.get("language_selector"))
886 .and_then(|s| s.as_str())
887 .unwrap_or("*");
888 let ExtId = Parameter
889 .get("extensionId")
890 .or_else(|| Parameter.get("extension_id"))
891 .and_then(|e| e.as_str())
892 .unwrap_or("");
893 let Scheme = Parameter.get("scheme").and_then(|s| s.as_str()).unwrap_or("");
896 let ProviderTypeName = MethodName
897 .strip_prefix("register_")
898 .map(|Stripped| Stripped.strip_suffix("_provider").unwrap_or(Stripped))
899 .unwrap_or("");
900 dev_log!(
907 "grpc-verbose",
908 "[MountainVinegRPCService] Cocoon registered {} provider: handle={}, lang={}",
909 ProviderTypeName,
910 Handle,
911 Selector
912 );
913 dev_log!(
914 "provider-register",
915 "[ProviderRegister] accepted method={} type={} handle={} lang={} scheme={} ext={}",
916 MethodName,
917 ProviderTypeName,
918 Handle,
919 Selector,
920 Scheme,
921 ExtId
922 );
923 use CommonLibrary::LanguageFeature::DTO::ProviderType::ProviderType as PT;
924 let ProvType = match ProviderTypeName {
925 "authentication" => Some(PT::Authentication),
926 "call_hierarchy" => Some(PT::CallHierarchy),
927 "code_actions" => Some(PT::CodeAction),
928 "code_lens" => Some(PT::CodeLens),
929 "color" => Some(PT::Color),
930 "completion_item" => Some(PT::Completion),
931 "debug_adapter" => Some(PT::DebugAdapter),
932 "debug_configuration" => Some(PT::DebugConfiguration),
933 "declaration" => Some(PT::Declaration),
934 "definition" => Some(PT::Definition),
935 "document_drop_edit" => Some(PT::DocumentDropEdit),
936 "document_formatting" => Some(PT::DocumentFormatting),
937 "document_highlight" => Some(PT::DocumentHighlight),
938 "document_link" => Some(PT::DocumentLink),
939 "document_paste_edit" => Some(PT::DocumentPasteEdit),
940 "document_range_formatting" => Some(PT::DocumentRangeFormatting),
941 "document_symbol" => Some(PT::DocumentSymbol),
942 "evaluatable_expression" => Some(PT::EvaluatableExpression),
943 "external_uri_opener" => Some(PT::ExternalUriOpener),
944 "file_decoration" => Some(PT::FileDecoration),
945 "file_system" => Some(PT::FileSystem),
946 "folding_range" => Some(PT::FoldingRange),
947 "hover" => Some(PT::Hover),
948 "implementation" => Some(PT::Implementation),
949 "inlay_hints" => Some(PT::InlayHint),
950 "inline_completion_item" => Some(PT::InlineCompletion),
951 "inline_edit" => Some(PT::InlineEdit),
952 "inline_values" => Some(PT::InlineValues),
953 "linked_editing_range" => Some(PT::LinkedEditingRange),
954 "mapped_edits" => Some(PT::MappedEdits),
955 "multi_document_highlight" => Some(PT::MultiDocumentHighlight),
956 "notebook_content" => Some(PT::NotebookContent),
957 "notebook_serializer" => Some(PT::NotebookSerializer),
958 "on_type_formatting" => Some(PT::OnTypeFormatting),
959 "reference" => Some(PT::References),
960 "remote_authority_resolver" => Some(PT::RemoteAuthorityResolver),
961 "rename" => Some(PT::Rename),
962 "resource_label_formatter" => Some(PT::ResourceLabelFormatter),
963 "scm" => Some(PT::SourceControl),
964 "scm_resource_group" => Some(PT::ScmResourceGroup),
965 "selection_range" => Some(PT::SelectionRange),
966 "semantic_tokens" => Some(PT::SemanticTokens),
967 "signature_help" => Some(PT::SignatureHelp),
968 "task" => Some(PT::Task),
969 "terminal_link" => Some(PT::TerminalLink),
970 "terminal_profile" => Some(PT::TerminalProfile),
971 "text_document_content" => Some(PT::TextDocumentContent),
972 "type_definition" => Some(PT::TypeDefinition),
973 "type_hierarchy" => Some(PT::TypeHierarchy),
974 "uri_handler" => Some(PT::UriHandler),
975 "workspace_symbol" => Some(PT::WorkspaceSymbol),
976 _ => None,
977 };
978 if let Some(ProviderType) = ProvType {
979 use crate::ApplicationState::DTO::ProviderRegistrationDTO::ProviderRegistrationDTO;
980 let SelectorValue = if !Scheme.is_empty() {
984 json!([{ "scheme": Scheme, "language": Selector }])
985 } else {
986 json!([{ "language": Selector }])
987 };
988 let Dto = ProviderRegistrationDTO {
989 Handle,
990 ProviderType,
991 Selector:SelectorValue,
992 SideCarIdentifier:"cocoon-main".to_string(),
993 ExtensionIdentifier:json!(ExtId),
994 Options:Parameter.get("options").cloned(),
995 };
996 self.RunTime
997 .Environment
998 .ApplicationState
999 .Extension
1000 .ProviderRegistration
1001 .RegisterProvider(Handle, Dto);
1002 }
1003 },
1004 _ => {
1005 dev_log!("grpc", "[MountainVinegRPCService] Cocoon notification: {}", MethodName);
1006 let PayloadPreview = if NotificationData.parameter.len() <= 160 {
1019 String::from_utf8_lossy(&NotificationData.parameter).into_owned()
1020 } else {
1021 let Slice = &NotificationData.parameter[..160];
1022 format!("{}…", String::from_utf8_lossy(Slice))
1023 };
1024 dev_log!(
1025 "notif-drop",
1026 "[NotifDrop] method={} payload_bytes={} preview={:?} (falls through to cocoon:{} event)",
1027 MethodName,
1028 NotificationData.parameter.len(),
1029 PayloadPreview,
1030 MethodName
1031 );
1032 let EventName = format!("cocoon:{}", MethodName);
1035 if let Err(Error) = self.ApplicationHandle.emit(&EventName, &Parameter) {
1036 dev_log!(
1037 "grpc",
1038 "warn: [MountainVinegRPCService] Failed to emit {}: {}",
1039 EventName,
1040 Error
1041 );
1042 }
1043 },
1044 }
1045
1046 Ok(Response::new(Empty {}))
1047 }
1048
1049 async fn cancel_operation(&self, request:Request<CancelOperationRequest>) -> Result<Response<Empty>, Status> {
1061 let cancel_request = request.into_inner();
1062
1063 let RequestIdentifierToCancel = cancel_request.request_identifier_to_cancel;
1064
1065 dev_log!(
1066 "grpc",
1067 "[MountainVinegRPCService] Received CancelOperation request for RequestID: {}",
1068 RequestIdentifierToCancel
1069 );
1070
1071 let cancel_token = {
1073 let operations = self.ActiveOperations.read().await;
1074 operations.get(&RequestIdentifierToCancel).cloned()
1075 };
1076
1077 match cancel_token {
1078 Some(token) => {
1079 token.cancel();
1081
1082 dev_log!(
1083 "grpc",
1084 "[MountainVinegRPCService] Successfully initiated cancellation for operation {}",
1085 RequestIdentifierToCancel
1086 );
1087
1088 Ok(Response::new(Empty {}))
1093 },
1094 None => {
1095 dev_log!(
1097 "grpc",
1098 "warn: [MountainVinegRPCService] Cannot cancel operation {}: operation not found (may have \
1099 already completed)",
1100 RequestIdentifierToCancel
1101 );
1102
1103 Ok(Response::new(Empty {}))
1105 },
1106 }
1107 }
1108}