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