mssf_core/client/
notification.rs

1// ------------------------------------------------------------
2// Copyright (c) Microsoft Corporation.  All rights reserved.
3// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
4// ------------------------------------------------------------
5
6use mssf_com::FabricClient::{
7    IFabricServiceEndpointsVersion, IFabricServiceNotification,
8    IFabricServiceNotificationEventHandler, IFabricServiceNotificationEventHandler_Impl,
9};
10
11use crate::types::{ServicePartitionInformation, Uri};
12
13use super::svc_mgmt_client::ResolvedServiceEndpoint;
14
15/// Rust trait to turn rust code into IFabricServiceNotificationEventHandler.
16/// Not exposed to user
17pub trait ServiceNotificationEventHandler: 'static {
18    fn on_notification(&self, notification: ServiceNotification) -> crate::Result<()>;
19}
20
21/// Content of the service notification callback.
22/// Remarks:
23/// If endpoint list is empty, the service is removed.
24#[derive(Debug, Clone)]
25pub struct ServiceNotification {
26    pub service_name: Uri,
27    pub partition_info: Option<ServicePartitionInformation>,
28    pub partition_id: crate::GUID,
29    pub endpoints: Vec<ResolvedServiceEndpoint>,
30    pub version: Option<ServiceEndpointsVersion>,
31}
32
33impl From<&IFabricServiceNotification> for ServiceNotification {
34    fn from(com: &IFabricServiceNotification) -> Self {
35        // SF guarantees this is not null.
36        let raw = unsafe { com.get_Notification().as_ref().unwrap() };
37        let endpoints = crate::iter::vec_from_raw_com(raw.EndpointCount as usize, raw.Endpoints);
38        let version = unsafe { com.GetVersion() }
39            .ok()
40            .map(ServiceEndpointsVersion::from);
41        Self {
42            service_name: Uri::from(raw.ServiceName),
43            partition_info: unsafe {
44                // It is possible for partition info to be null,
45                // that is why we make the field as an option.
46                // See: https://github.com/microsoft/service-fabric/blob/93545a62e8f6c2407bd538c0f092b33419f77c30/src/prod/src/client/ServiceNotificationResult.cpp#L120
47                raw.PartitionInfo
48                    .as_ref()
49                    .map(ServicePartitionInformation::from)
50            },
51            partition_id: raw.PartitionId,
52            endpoints,
53            version,
54        }
55    }
56}
57
58/// IFabricServiceEndpointsVersion wrapper.
59#[derive(Debug, Clone)]
60pub struct ServiceEndpointsVersion {
61    com: IFabricServiceEndpointsVersion,
62}
63
64impl From<IFabricServiceEndpointsVersion> for ServiceEndpointsVersion {
65    fn from(com: IFabricServiceEndpointsVersion) -> Self {
66        Self { com }
67    }
68}
69
70impl From<ServiceEndpointsVersion> for IFabricServiceEndpointsVersion {
71    fn from(value: ServiceEndpointsVersion) -> Self {
72        value.com
73    }
74}
75
76impl ServiceEndpointsVersion {
77    /// CSharp doc: Zero if this and other are equivalent,
78    /// a negative value if this is less than other, and a positive value if this is greater than other.
79    ///
80    /// This is not usually used in CSharp apps, but the implementation is provided here for completeness.
81    /// Ideally one should use mssf_core::client::svc_mgmt_client::ResolvedServicePartition instead, by
82    /// doing an additional FabricClient resolve call to retrieve from FabricClient cache.
83    pub fn compare(&self, other: &ServiceEndpointsVersion) -> crate::Result<i32> {
84        unsafe { self.com.Compare(&other.com) }.map_err(crate::Error::from)
85    }
86}
87
88impl PartialEq for ServiceEndpointsVersion {
89    fn eq(&self, other: &Self) -> bool {
90        match self.compare(other) {
91            Ok(i) => i == 0,
92            Err(_) => false, // error comparing different services
93        }
94    }
95}
96
97impl PartialOrd for ServiceEndpointsVersion {
98    /// Compare the version of the resolved result.
99    /// a > b means partial_cmp(a,b) == Some(Greater) i.e. a.compare_version(b) > 0.
100    /// a is newer and up to date.
101    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
102        match self.compare(other) {
103            Ok(i) => Some(i.cmp(&0)),
104            // If you compare version of different service you get error
105            Err(_) => None,
106        }
107    }
108}
109
110// Bridge implementation for the notification handler to turn rust code into SF com object.
111#[windows_core::implement(IFabricServiceNotificationEventHandler)]
112#[allow(non_camel_case_types)] // Suppress lint for _Impl struct
113pub struct ServiceNotificationEventHandlerBridge<T>
114where
115    T: ServiceNotificationEventHandler,
116{
117    inner: T,
118}
119
120impl<T> ServiceNotificationEventHandlerBridge<T>
121where
122    T: ServiceNotificationEventHandler,
123{
124    pub fn new(inner: T) -> Self {
125        Self { inner }
126    }
127
128    pub fn new_com(inner: T) -> IFabricServiceNotificationEventHandler {
129        Self::new(inner).into()
130    }
131}
132
133impl<T> IFabricServiceNotificationEventHandler_Impl
134    for ServiceNotificationEventHandlerBridge_Impl<T>
135where
136    T: ServiceNotificationEventHandler,
137{
138    fn OnNotification(
139        &self,
140        notification: windows_core::Ref<IFabricServiceNotification>,
141    ) -> crate::WinResult<()> {
142        let com = notification.unwrap();
143        let msg = ServiceNotification::from(com);
144        self.inner
145            .on_notification(msg)
146            .map_err(crate::WinError::from)
147    }
148}
149
150/// Lambda implemnentation of ServiceNotificationEventHandler trait.
151/// This is used in FabricClientBuilder to build function into handler.
152/// Not exposed to user.
153/// This isn't strictly required by the implementation as written. But it leaves open the door to non-lambda implementations in future.
154pub struct LambdaServiceNotificationHandler<T>
155where
156    T: Fn(ServiceNotification) -> crate::Result<()> + 'static,
157{
158    f: T,
159}
160
161impl<T> LambdaServiceNotificationHandler<T>
162where
163    T: Fn(ServiceNotification) -> crate::Result<()> + 'static,
164{
165    pub fn new(f: T) -> Self {
166        Self { f }
167    }
168}
169
170impl<T> ServiceNotificationEventHandler for LambdaServiceNotificationHandler<T>
171where
172    T: Fn(ServiceNotification) -> crate::Result<()> + 'static,
173{
174    fn on_notification(&self, notification: ServiceNotification) -> crate::Result<()> {
175        (self.f)(notification)
176    }
177}