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 pub const MAX_CONCURRENT_OPERATIONS:usize = 50;
64
65 pub const CANCELLATION_TIMEOUT_MS:u64 = 5000;
67
68 pub const MAX_METHOD_NAME_LENGTH:usize = 128;
70}
71
72pub struct MountainVinegRPCService {
78 ApplicationHandle:AppHandle,
80
81 RunTime:Arc<ApplicationRunTime>,
83
84 ActiveOperations:Arc<RwLock<HashMap<u64, tokio_util::sync::CancellationToken>>>,
87}
88
89impl MountainVinegRPCService {
90 pub fn ApplicationHandle(&self) -> &AppHandle { &self.ApplicationHandle }
96
97 pub fn RunTime(&self) -> &Arc<ApplicationRunTime> { &self.RunTime }
102}
103
104impl MountainVinegRPCService {
105 pub fn Create(ApplicationHandle:AppHandle, RunTime:Arc<ApplicationRunTime>) -> Self {
114 dev_log!("grpc", "[MountainVinegRPCService] New instance created");
115
116 Self {
117 ApplicationHandle,
118 RunTime,
119 ActiveOperations:Arc::new(RwLock::new(HashMap::new())),
120 }
121 }
122
123 pub async fn RegisterOperation(&self, request_id:u64) -> tokio_util::sync::CancellationToken {
131 let token = tokio_util::sync::CancellationToken::new();
132 self.ActiveOperations.write().await.insert(request_id, token.clone());
133 dev_log!(
134 "grpc",
135 "[MountainVinegRPCService] Registered operation {} for cancellation",
136 request_id
137 );
138 token
139 }
140
141 pub async fn UnregisterOperation(&self, request_id:u64) {
146 self.ActiveOperations.write().await.remove(&request_id);
147 dev_log!("grpc", "[MountainVinegRPCService] Unregistered operation {}", request_id);
148 }
149
150 fn ValidateRequest(&self, request:&GenericRequest) -> Result<(), Status> {
159 if request.method.is_empty() {
161 return Err(Status::invalid_argument("Method name cannot be empty"));
162 }
163
164 if request.method.len() > ServiceConfig::MAX_METHOD_NAME_LENGTH {
165 return Err(Status::invalid_argument(format!(
166 "Method name exceeds maximum length of {} characters",
167 ServiceConfig::MAX_METHOD_NAME_LENGTH
168 )));
169 }
170
171 if request.parameter.len() > 4 * 1024 * 1024 {
173 return Err(Status::resource_exhausted("Request parameter size exceeds limit"));
174 }
175
176 if request.method.contains("../") || request.method.contains("::") {
178 return Err(Status::permission_denied("Invalid method name format"));
179 }
180
181 Ok(())
182 }
183
184 fn CreateErrorResponse(RequestIdentifier:u64, code:i32, message:String, data:Option<Vec<u8>>) -> GenericResponse {
195 GenericResponse {
196 request_identifier:RequestIdentifier,
197 result:vec![],
198 error:Some(RPCError { code, message, data:data.unwrap_or_default() }),
199 }
200 }
201
202 fn CreateSuccessResponse(RequestIdentifier:u64, result:&Value) -> GenericResponse {
211 let result_bytes = match serde_json::to_vec(result) {
212 Ok(bytes) => bytes,
213 Err(e) => {
214 dev_log!("grpc", "error: [MountainVinegRPCService] Failed to serialize result: {}", e);
215
216 return Self::CreateErrorResponse(
218 RequestIdentifier,
219 -32603, "Failed to serialize response".to_string(),
221 None,
222 );
223 },
224 };
225
226 GenericResponse { request_identifier:RequestIdentifier, result:result_bytes, error:None }
227 }
228}
229
230#[tonic::async_trait]
231impl MountainService for MountainVinegRPCService {
232 type OpenChannelFromCocoonStream = std::pin::Pin<
238 Box<
239 dyn tonic::codegen::tokio_stream::Stream<Item = Result<crate::Vine::Generated::Envelope, tonic::Status>>
240 + Send
241 + 'static,
242 >,
243 >;
244
245 async fn open_channel_from_cocoon(
246 &self,
247 _request:tonic::Request<tonic::Streaming<crate::Vine::Generated::Envelope>>,
248 ) -> Result<tonic::Response<Self::OpenChannelFromCocoonStream>, tonic::Status> {
249 Err(tonic::Status::unimplemented(
250 "OpenChannelFromCocoon: streaming multiplexer not yet wired (Patch 14); use unary endpoints",
251 ))
252 }
253
254 async fn process_cocoon_request(
269 &self,
270 request:Request<GenericRequest>,
271 ) -> Result<Response<GenericResponse>, Status> {
272 let RequestData = request.into_inner();
273
274 let MethodName = RequestData.method.clone();
275
276 let RequestIdentifier = RequestData.request_identifier;
277 let ReceiveInstant = std::time::Instant::now();
278
279 dev_log!(
284 "grpc-verbose",
285 "[MountainVinegRPCService] Received gRPC Request [ID: {}]: Method='{}'",
286 RequestIdentifier,
287 MethodName
288 );
289
290 let IsHotRpc = matches!(
299 MethodName.as_str(),
300 "$tree:register" | "tree.register" | "Configuration.Inspect" | "Command.Execute"
301 );
302 if IsHotRpc {
303 let InstrumentRecvNs = std::time::SystemTime::now()
304 .duration_since(std::time::UNIX_EPOCH)
305 .map(|D| D.as_nanos())
306 .unwrap_or(0);
307 dev_log!(
311 "rpc-latency",
312 "[LandFix:RPC] grpc-recv method={} id={} size={} t_ns={}",
313 MethodName,
314 RequestIdentifier,
315 RequestData.parameter.len(),
316 InstrumentRecvNs
317 );
318 }
319
320 if let Err(status) = self.ValidateRequest(&RequestData) {
322 dev_log!("grpc", "warn: [MountainVinegRPCService] Request validation failed: {}", status);
323
324 return Ok(Response::new(Self::CreateErrorResponse(
325 RequestIdentifier,
326 -32602, status.message().to_string(),
328 None,
329 )));
330 }
331
332 let ParametersValue:Value = match serde_json::from_slice(&RequestData.parameter) {
334 Ok(v) => {
335 dev_log!(
348 "grpc-verbose",
349 "[MountainVinegRPCService] Params for [ID: {}] ({} bytes)",
350 RequestIdentifier,
351 RequestData.parameter.len()
352 );
353 v
354 },
355 Err(e) => {
356 let msg = format!("Failed to deserialize parameters for method '{}': {}", MethodName, e);
357
358 dev_log!("grpc", "error: {}", msg);
359
360 return Ok(Response::new(Self::CreateErrorResponse(
361 RequestIdentifier,
362 -32700, msg,
364 None,
365 )));
366 },
367 };
368
369 dev_log!(
370 "grpc-verbose",
371 "[MountainVinegRPCService] Dispatching request [ID: {}] to Track::DispatchLogic",
372 RequestIdentifier
373 );
374
375 let DispatchResult = Track::SideCarRequest::DispatchSideCarRequest::DispatchSideCarRequest(
377 self.ApplicationHandle.clone(),
378 self.RunTime.clone(),
379 "cocoon-main".to_string(),
381 MethodName.clone(),
382 ParametersValue,
383 )
384 .await;
385
386 match DispatchResult {
387 Ok(SuccessfulResult) => {
388 if IsHotRpc {
389 dev_log!(
393 "rpc-latency",
394 "[LandFix:RPC] dispatched method={} id={} elapsed={}ms",
395 MethodName,
396 RequestIdentifier,
397 ReceiveInstant.elapsed().as_millis()
398 );
399 }
400 dev_log!(
405 "grpc-verbose",
406 "[MountainVinegRPCService] Request [ID: {}] completed successfully",
407 RequestIdentifier
408 );
409
410 Ok(Response::new(Self::CreateSuccessResponse(RequestIdentifier, &SuccessfulResult)))
411 },
412
413 Err(ErrorString) => {
414 let LowerError = ErrorString.to_lowercase();
425 let LooksLike404 = (MethodName == "FileSystem.ReadFile"
437 || MethodName == "FileSystem.Stat"
438 || MethodName == "FileSystem.ReadDirectory")
439 && (LowerError.contains("resource not found")
440 || LowerError.contains("not found")
441 || LowerError.contains("enoent")
442 || LowerError.contains("no such file or directory")
443 || LowerError.contains("entity not found")
444 || LowerError.contains("os error 2")
445 || LowerError.contains("path is outside of the registered workspace")
446 || LowerError.contains("permission denied for operation")
447 || LowerError.contains("workspace is not trusted"));
448 if LooksLike404 {
449 dev_log!(
450 "grpc-verbose",
451 "[LandFix:MountainVinegRPC] Request [ID: {}] {} 404 (benign): {}",
452 RequestIdentifier,
453 MethodName,
454 ErrorString
455 );
456 } else {
457 dev_log!(
458 "grpc",
459 "error: [MountainVinegRPCService] Request [ID: {}] failed: {}",
460 RequestIdentifier,
461 ErrorString
462 );
463 }
464
465 let ErrorCode = if LooksLike404 { -32004 } else { -32000 };
469 Ok(Response::new(Self::CreateErrorResponse(
470 RequestIdentifier,
471 ErrorCode,
472 ErrorString,
473 None,
474 )))
475 },
476 }
477 }
478
479 async fn send_cocoon_notification(&self, request:Request<GenericNotification>) -> Result<Response<Empty>, Status> {
499 let NotificationData = request.into_inner();
500
501 let MethodName = NotificationData.method;
502
503 dev_log!(
507 "grpc-verbose",
508 "[MountainVinegRPCService] Received gRPC Notification: Method='{}'",
509 MethodName
510 );
511
512 if MethodName.is_empty() {
514 dev_log!(
515 "grpc",
516 "warn: [MountainVinegRPCService] Received notification with empty method name"
517 );
518 return Err(Status::invalid_argument("Method name cannot be empty"));
519 }
520
521 let Parameter:Value = if NotificationData.parameter.is_empty() {
532 Value::Null
533 } else {
534 serde_json::from_slice(&NotificationData.parameter).unwrap_or(Value::Null)
535 };
536
537 match MethodName.as_str() {
538 "extensionHostMessage" => {
543 super::Notification::ExtensionHostMessage::ExtensionHostMessage(self, &Parameter).await;
544 },
545 "ExtensionActivated" => {
546 super::Notification::ExtensionActivated::ExtensionActivated(self, &Parameter).await;
547 },
548 "ExtensionDeactivated" => {
549 super::Notification::ExtensionDeactivated::ExtensionDeactivated(self, &Parameter).await;
550 },
551 "WebviewReady" => {
552 super::Notification::WebviewReady::WebviewReady(self, &Parameter).await;
553 },
554 "progress.start" => {
555 super::Notification::ProgressStart::ProgressStart(self, &Parameter).await;
556 },
557 "progress.report" => {
558 super::Notification::ProgressReport::ProgressReport(self, &Parameter).await;
559 },
560 "progress.end" => {
561 super::Notification::ProgressEnd::ProgressEnd(self, &Parameter).await;
562 },
563 "languages.setDocumentLanguage" => {
564 super::Notification::LanguagesSetDocumentLanguage::LanguagesSetDocumentLanguage(self, &Parameter).await;
565 },
566 "workspace.applyEdit" => {
567 super::Notification::WorkspaceApplyEdit::WorkspaceApplyEdit(self, &Parameter).await;
568 },
569 "window.showTextDocument" => {
570 super::Notification::WindowShowTextDocument::WindowShowTextDocument(self, &Parameter).await;
571 },
572
573 "webview.setTitle"
579 | "webview.setIconPath"
580 | "webview.setHtml"
581 | "webview.updateView"
582 | "webview.reveal" => {
583 super::Notification::WebviewLifecycle::WebviewLifecycle(self, &MethodName, &Parameter).await;
584 },
585 "window.createTerminal" => {
586 super::Notification::WindowCreateTerminal::WindowCreateTerminal(self, &Parameter).await;
587 },
588 "terminal.sendText" | "terminal.show" | "terminal.hide" | "terminal.dispose" => {
589 super::Notification::TerminalLifecycle::TerminalLifecycle(self, &MethodName, &Parameter).await;
590 },
591 "window.createTextEditorDecorationType" | "window.disposeTextEditorDecorationType" => {
592 super::Notification::DecorationTypeLifecycle::DecorationTypeLifecycle(self, &MethodName, &Parameter).await;
593 },
594 "debug.addBreakpoints" | "debug.removeBreakpoints" | "debug.consoleAppend" => {
595 super::Notification::DebugLifecycle::DebugLifecycle(self, &MethodName, &Parameter).await;
596 },
597 "statusBar.update" | "statusBar.dispose" => {
598 super::Notification::StatusBarLifecycle::StatusBarLifecycle(self, &MethodName, &Parameter).await;
599 },
600 "statusBar.message" => {
601 super::Notification::StatusBarMessage::StatusBarMessage(self, &Parameter).await;
602 },
603 "window.showMessage" => {
604 super::Notification::WindowShowMessage::WindowShowMessage(self, &Parameter).await;
605 },
606 "registerCommand" => {
607 super::Notification::RegisterCommand::RegisterCommand(self, &Parameter).await;
608 },
609 "unregisterCommand" => {
610 super::Notification::UnregisterCommand::UnregisterCommand(self, &Parameter).await;
611 },
612
613 "unregister_authentication_provider" => {
623 super::Notification::UnregisterAuthenticationProvider::UnregisterAuthenticationProvider(self, &Parameter).await;
624 },
625 "unregister_debug_adapter" => {
626 super::Notification::UnregisterDebugAdapter::UnregisterDebugAdapter(self, &Parameter).await;
627 },
628 "unregister_file_system_provider" => {
629 super::Notification::UnregisterFileSystemProvider::UnregisterFileSystemProvider(self, &Parameter).await;
630 },
631 "unregister_scm_provider" => {
632 super::Notification::UnregisterScmProvider::UnregisterScmProvider(self, &Parameter).await;
633 },
634 "unregister_task_provider" => {
635 super::Notification::UnregisterTaskProvider::UnregisterTaskProvider(self, &Parameter).await;
636 },
637 "unregister_uri_handler" => {
638 super::Notification::UnregisterUriHandler::UnregisterUriHandler(self, &Parameter).await;
639 },
640 "update_scm_group" => {
641 super::Notification::UpdateScmGroup::UpdateScmGroup(self, &Parameter).await;
642 },
643 "register_scm_provider" => {
654 super::Notification::RegisterScmProvider::RegisterScmProvider(self, &Parameter).await;
655 },
656 "register_scm_resource_group" => {
657 super::Notification::RegisterScmResourceGroup::RegisterScmResourceGroup(self, &Parameter).await;
658 },
659
660 "progress.update" => {
662 super::Notification::ProgressUpdate::ProgressUpdate(self, &Parameter).await;
663 },
664 "progress.complete" => {
665 super::Notification::ProgressComplete::ProgressComplete(self, &Parameter).await;
666 },
667
668 "setStatusBarText" => {
670 super::Notification::SetStatusBarText::SetStatusBarText(self, &Parameter).await;
671 },
672 "disposeStatusBarItem" => {
673 super::Notification::DisposeStatusBarItem::DisposeStatusBarItem(self, &Parameter).await;
674 },
675
676 "output.create" => {
681 super::Notification::OutputCreate::OutputCreate(self, &Parameter).await;
682 },
683 "output.append" => {
684 super::Notification::OutputAppend::OutputAppend(self, &Parameter).await;
685 },
686 "output.appendLine" => {
687 super::Notification::OutputAppendLine::OutputAppendLine(self, &Parameter).await;
688 },
689 "output.clear" => {
690 super::Notification::OutputClear::OutputClear(self, &Parameter).await;
691 },
692 "output.show" => {
693 super::Notification::OutputShow::OutputShow(self, &Parameter).await;
694 },
695 "output.dispose" => {
696 super::Notification::OutputDispose::OutputDispose(self, &Parameter).await;
697 },
698 "output.replace" => {
699 super::Notification::OutputReplace::OutputReplace(self, &Parameter).await;
700 },
701 "outputChannel.create" => {
702 super::Notification::OutputChannelCreate::OutputChannelCreate(self, &Parameter).await;
703 },
704 "outputChannel.append" => {
705 super::Notification::OutputChannelAppend::OutputChannelAppend(self, &Parameter).await;
706 },
707 "outputChannel.clear" => {
708 super::Notification::OutputChannelClear::OutputChannelClear(self, &Parameter).await;
709 },
710 "outputChannel.show" => {
711 super::Notification::OutputChannelShow::OutputChannelShow(self, &Parameter).await;
712 },
713 "outputChannel.hide" => {
714 super::Notification::OutputChannelHide::OutputChannelHide(self, &Parameter).await;
715 },
716 "outputChannel.dispose" => {
717 super::Notification::OutputChannelDispose::OutputChannelDispose(self, &Parameter).await;
718 },
719
720 "webview.postMessage" => {
722 super::Notification::WebviewPostMessage::WebviewPostMessage(self, &Parameter).await;
723 },
724 "webview.dispose" => {
725 super::Notification::WebviewDispose::WebviewDispose(self, &Parameter).await;
726 },
727
728 "set_language_configuration" => {
730 super::Notification::SetLanguageConfiguration::SetLanguageConfiguration(self, &Parameter).await;
731 },
732 "openExternal" => {
733 super::Notification::OpenExternal::OpenExternal(self, &Parameter).await;
734 },
735 "security.incident" => {
736 super::Notification::SecurityIncident::SecurityIncident(self, &Parameter).await;
737 },
738
739 "register_authentication_provider"
761 | "register_call_hierarchy_provider"
762 | "register_code_actions_provider"
763 | "register_code_lens_provider"
764 | "register_color_provider"
765 | "register_completion_item_provider"
766 | "register_debug_adapter"
767 | "register_debug_configuration_provider"
768 | "register_declaration_provider"
769 | "register_definition_provider"
770 | "register_document_drop_edit_provider"
771 | "register_document_formatting_provider"
772 | "register_document_highlight_provider"
773 | "register_document_link_provider"
774 | "register_document_paste_edit_provider"
775 | "register_document_range_formatting_provider"
776 | "register_document_symbol_provider"
777 | "register_evaluatable_expression_provider"
778 | "register_external_uri_opener"
779 | "register_file_decoration_provider"
780 | "register_file_system_provider"
781 | "register_folding_range_provider"
782 | "register_hover_provider"
783 | "register_implementation_provider"
784 | "register_inlay_hints_provider"
785 | "register_inline_completion_item_provider"
786 | "register_inline_edit_provider"
787 | "register_inline_values_provider"
788 | "register_linked_editing_range_provider"
789 | "register_mapped_edits_provider"
790 | "register_multi_document_highlight_provider"
791 | "register_notebook_content_provider"
792 | "register_notebook_serializer"
793 | "register_on_type_formatting_provider"
794 | "register_reference_provider"
795 | "register_remote_authority_resolver"
796 | "register_rename_provider"
797 | "register_resource_label_formatter"
798 | "register_selection_range_provider"
805 | "register_semantic_tokens_provider"
806 | "register_signature_help_provider"
807 | "register_task_provider"
808 | "register_terminal_link_provider"
809 | "register_terminal_profile_provider"
810 | "register_text_document_content_provider"
811 | "register_type_definition_provider"
812 | "register_type_hierarchy_provider"
813 | "register_uri_handler"
814 | "register_workspace_symbol_provider" => {
815 let Handle = Parameter.get("handle").and_then(|h| h.as_u64()).unwrap_or(0) as u32;
816 let Selector = Parameter
823 .get("languageSelector")
824 .or_else(|| Parameter.get("language_selector"))
825 .and_then(|s| s.as_str())
826 .unwrap_or("*");
827 let ExtId = Parameter
828 .get("extensionId")
829 .or_else(|| Parameter.get("extension_id"))
830 .and_then(|e| e.as_str())
831 .unwrap_or("");
832 let Scheme = Parameter.get("scheme").and_then(|s| s.as_str()).unwrap_or("");
835 let ProviderTypeName = MethodName
836 .strip_prefix("register_")
837 .map(|Stripped| Stripped.strip_suffix("_provider").unwrap_or(Stripped))
838 .unwrap_or("");
839 dev_log!(
846 "grpc-verbose",
847 "[MountainVinegRPCService] Cocoon registered {} provider: handle={}, lang={}",
848 ProviderTypeName,
849 Handle,
850 Selector
851 );
852 dev_log!(
853 "provider-register",
854 "[ProviderRegister] accepted method={} type={} handle={} lang={} scheme={} ext={}",
855 MethodName,
856 ProviderTypeName,
857 Handle,
858 Selector,
859 Scheme,
860 ExtId
861 );
862 use CommonLibrary::LanguageFeature::DTO::ProviderType::ProviderType as PT;
863 let ProvType = match ProviderTypeName {
864 "authentication" => Some(PT::Authentication),
865 "call_hierarchy" => Some(PT::CallHierarchy),
866 "code_actions" => Some(PT::CodeAction),
867 "code_lens" => Some(PT::CodeLens),
868 "color" => Some(PT::Color),
869 "completion_item" => Some(PT::Completion),
870 "debug_adapter" => Some(PT::DebugAdapter),
871 "debug_configuration" => Some(PT::DebugConfiguration),
872 "declaration" => Some(PT::Declaration),
873 "definition" => Some(PT::Definition),
874 "document_drop_edit" => Some(PT::DocumentDropEdit),
875 "document_formatting" => Some(PT::DocumentFormatting),
876 "document_highlight" => Some(PT::DocumentHighlight),
877 "document_link" => Some(PT::DocumentLink),
878 "document_paste_edit" => Some(PT::DocumentPasteEdit),
879 "document_range_formatting" => Some(PT::DocumentRangeFormatting),
880 "document_symbol" => Some(PT::DocumentSymbol),
881 "evaluatable_expression" => Some(PT::EvaluatableExpression),
882 "external_uri_opener" => Some(PT::ExternalUriOpener),
883 "file_decoration" => Some(PT::FileDecoration),
884 "file_system" => Some(PT::FileSystem),
885 "folding_range" => Some(PT::FoldingRange),
886 "hover" => Some(PT::Hover),
887 "implementation" => Some(PT::Implementation),
888 "inlay_hints" => Some(PT::InlayHint),
889 "inline_completion_item" => Some(PT::InlineCompletion),
890 "inline_edit" => Some(PT::InlineEdit),
891 "inline_values" => Some(PT::InlineValues),
892 "linked_editing_range" => Some(PT::LinkedEditingRange),
893 "mapped_edits" => Some(PT::MappedEdits),
894 "multi_document_highlight" => Some(PT::MultiDocumentHighlight),
895 "notebook_content" => Some(PT::NotebookContent),
896 "notebook_serializer" => Some(PT::NotebookSerializer),
897 "on_type_formatting" => Some(PT::OnTypeFormatting),
898 "reference" => Some(PT::References),
899 "remote_authority_resolver" => Some(PT::RemoteAuthorityResolver),
900 "rename" => Some(PT::Rename),
901 "resource_label_formatter" => Some(PT::ResourceLabelFormatter),
902 "scm" => Some(PT::SourceControl),
903 "scm_resource_group" => Some(PT::ScmResourceGroup),
904 "selection_range" => Some(PT::SelectionRange),
905 "semantic_tokens" => Some(PT::SemanticTokens),
906 "signature_help" => Some(PT::SignatureHelp),
907 "task" => Some(PT::Task),
908 "terminal_link" => Some(PT::TerminalLink),
909 "terminal_profile" => Some(PT::TerminalProfile),
910 "text_document_content" => Some(PT::TextDocumentContent),
911 "type_definition" => Some(PT::TypeDefinition),
912 "type_hierarchy" => Some(PT::TypeHierarchy),
913 "uri_handler" => Some(PT::UriHandler),
914 "workspace_symbol" => Some(PT::WorkspaceSymbol),
915 _ => None,
916 };
917 if let Some(ProviderType) = ProvType {
918 use crate::ApplicationState::DTO::ProviderRegistrationDTO::ProviderRegistrationDTO;
919 let SelectorValue = if !Scheme.is_empty() {
923 json!([{ "scheme": Scheme, "language": Selector }])
924 } else {
925 json!([{ "language": Selector }])
926 };
927 let Dto = ProviderRegistrationDTO {
928 Handle,
929 ProviderType,
930 Selector:SelectorValue,
931 SideCarIdentifier:"cocoon-main".to_string(),
932 ExtensionIdentifier:json!(ExtId),
933 Options:Parameter.get("options").cloned(),
934 };
935 self.RunTime
936 .Environment
937 .ApplicationState
938 .Extension
939 .ProviderRegistration
940 .RegisterProvider(Handle, Dto);
941 }
942 },
943 _ => {
944 dev_log!("grpc", "[MountainVinegRPCService] Cocoon notification: {}", MethodName);
945 let PayloadPreview = if NotificationData.parameter.len() <= 160 {
958 String::from_utf8_lossy(&NotificationData.parameter).into_owned()
959 } else {
960 let Slice = &NotificationData.parameter[..160];
961 format!("{}…", String::from_utf8_lossy(Slice))
962 };
963 dev_log!(
964 "notif-drop",
965 "[NotifDrop] method={} payload_bytes={} preview={:?} (falls through to cocoon:{} event)",
966 MethodName,
967 NotificationData.parameter.len(),
968 PayloadPreview,
969 MethodName
970 );
971 let EventName = format!("cocoon:{}", MethodName);
974 if let Err(Error) = self.ApplicationHandle.emit(&EventName, &Parameter) {
975 dev_log!(
976 "grpc",
977 "warn: [MountainVinegRPCService] Failed to emit {}: {}",
978 EventName,
979 Error
980 );
981 }
982 },
983 }
984
985 Ok(Response::new(Empty {}))
986 }
987
988 async fn cancel_operation(&self, request:Request<CancelOperationRequest>) -> Result<Response<Empty>, Status> {
1000 let cancel_request = request.into_inner();
1001
1002 let RequestIdentifierToCancel = cancel_request.request_identifier_to_cancel;
1003
1004 dev_log!(
1005 "grpc",
1006 "[MountainVinegRPCService] Received CancelOperation request for RequestID: {}",
1007 RequestIdentifierToCancel
1008 );
1009
1010 let cancel_token = {
1012 let operations = self.ActiveOperations.read().await;
1013 operations.get(&RequestIdentifierToCancel).cloned()
1014 };
1015
1016 match cancel_token {
1017 Some(token) => {
1018 token.cancel();
1020
1021 dev_log!(
1022 "grpc",
1023 "[MountainVinegRPCService] Successfully initiated cancellation for operation {}",
1024 RequestIdentifierToCancel
1025 );
1026
1027 Ok(Response::new(Empty {}))
1032 },
1033 None => {
1034 dev_log!(
1036 "grpc",
1037 "warn: [MountainVinegRPCService] Cannot cancel operation {}: operation not found (may have \
1038 already completed)",
1039 RequestIdentifierToCancel
1040 );
1041
1042 Ok(Response::new(Empty {}))
1044 },
1045 }
1046 }
1047}