1use std::{ffi::c_void, time::Duration};
7
8use crate::{PCWSTR, WString, runtime::executor::BoxedCancelToken, types::Uri};
9use mssf_com::{
10 FabricClient::{IFabricResolvedServicePartitionResult, IFabricServiceManagementClient6},
11 FabricTypes::{
12 FABRIC_PARTITION_KEY_TYPE, FABRIC_PARTITION_KEY_TYPE_INT64,
13 FABRIC_PARTITION_KEY_TYPE_INVALID, FABRIC_PARTITION_KEY_TYPE_NONE,
14 FABRIC_PARTITION_KEY_TYPE_STRING, FABRIC_REMOVE_REPLICA_DESCRIPTION,
15 FABRIC_RESOLVED_SERVICE_ENDPOINT, FABRIC_RESTART_REPLICA_DESCRIPTION,
16 FABRIC_SERVICE_DESCRIPTION, FABRIC_SERVICE_ENDPOINT_ROLE,
17 FABRIC_SERVICE_NOTIFICATION_FILTER_DESCRIPTION, FABRIC_SERVICE_PARTITION_KIND,
18 FABRIC_SERVICE_PARTITION_KIND_INT64_RANGE, FABRIC_SERVICE_PARTITION_KIND_INVALID,
19 FABRIC_SERVICE_PARTITION_KIND_NAMED, FABRIC_SERVICE_PARTITION_KIND_SINGLETON,
20 FABRIC_SERVICE_ROLE_INVALID, FABRIC_SERVICE_ROLE_STATEFUL_PRIMARY,
21 FABRIC_SERVICE_ROLE_STATEFUL_SECONDARY, FABRIC_SERVICE_ROLE_STATELESS,
22 FABRIC_SERVICE_UPDATE_DESCRIPTION, FABRIC_URI,
23 },
24};
25
26use crate::sync::{FabricReceiver, fabric_begin_end_proxy};
27
28use crate::{
29 iter::{FabricIter, FabricListAccessor},
30 strings::WStringWrap,
31 types::{
32 RemoveReplicaDescription, RestartReplicaDescription, ServiceNotificationFilterDescription,
33 },
34};
35
36#[derive(Debug, Clone)]
38pub struct ServiceManagementClient {
39 com: IFabricServiceManagementClient6,
40}
41
42impl ServiceManagementClient {
43 pub fn get_com(&self) -> IFabricServiceManagementClient6 {
44 self.com.clone()
45 }
46}
47impl ServiceManagementClient {
50 fn resolve_service_partition_internal(
51 &self,
52 name: FABRIC_URI,
53 partition_key_type: FABRIC_PARTITION_KEY_TYPE,
54 partition_key: Option<*const ::core::ffi::c_void>,
55 previous_result: Option<&IFabricResolvedServicePartitionResult>, timeout_milliseconds: u32,
57 cancellation_token: Option<BoxedCancelToken>,
58 ) -> FabricReceiver<crate::WinResult<IFabricResolvedServicePartitionResult>> {
59 let com1 = &self.com;
60 let com2 = self.com.clone();
61 fabric_begin_end_proxy(
62 move |callback| unsafe {
63 com1.BeginResolveServicePartition(
64 name,
65 partition_key_type,
66 partition_key.unwrap_or(std::ptr::null()),
67 previous_result,
68 timeout_milliseconds,
69 callback,
70 )
71 },
72 move |ctx| unsafe { com2.EndResolveServicePartition(ctx) },
73 cancellation_token,
74 )
75 }
76
77 fn restart_replica_internal(
78 &self,
79 desc: &FABRIC_RESTART_REPLICA_DESCRIPTION,
80 timeout_milliseconds: u32,
81 cancellation_token: Option<BoxedCancelToken>,
82 ) -> FabricReceiver<crate::WinResult<()>> {
83 let com1 = &self.com;
84 let com2 = self.com.clone();
85 fabric_begin_end_proxy(
86 move |callback| unsafe {
87 com1.BeginRestartReplica(desc, timeout_milliseconds, callback)
88 },
89 move |ctx| unsafe { com2.EndRestartReplica(ctx) },
90 cancellation_token,
91 )
92 }
93
94 fn remove_replica_internal(
95 &self,
96 desc: &FABRIC_REMOVE_REPLICA_DESCRIPTION,
97 timeout_milliseconds: u32,
98 cancellation_token: Option<BoxedCancelToken>,
99 ) -> FabricReceiver<crate::WinResult<()>> {
100 let com1 = &self.com;
101 let com2 = self.com.clone();
102 fabric_begin_end_proxy(
103 move |callback| unsafe {
104 com1.BeginRemoveReplica(desc, timeout_milliseconds, callback)
105 },
106 move |ctx| unsafe { com2.EndRemoveReplica(ctx) },
107 cancellation_token,
108 )
109 }
110
111 fn register_service_notification_filter_internal(
112 &self,
113 desc: &FABRIC_SERVICE_NOTIFICATION_FILTER_DESCRIPTION,
114 timeout_milliseconds: u32,
115 cancellation_token: Option<BoxedCancelToken>,
116 ) -> FabricReceiver<crate::WinResult<i64>> {
117 let com1 = &self.com;
118 let com2 = self.com.clone();
119 fabric_begin_end_proxy(
120 move |callback| unsafe {
121 com1.BeginRegisterServiceNotificationFilter(desc, timeout_milliseconds, callback)
122 },
123 move |ctx| unsafe { com2.EndRegisterServiceNotificationFilter(ctx) },
124 cancellation_token,
125 )
126 }
127
128 fn unregister_service_notification_filter_internal(
129 &self,
130 filterid: i64,
131 timeout_milliseconds: u32,
132 cancellation_token: Option<BoxedCancelToken>,
133 ) -> FabricReceiver<crate::WinResult<()>> {
134 let com1 = &self.com;
135 let com2 = self.com.clone();
136 fabric_begin_end_proxy(
137 move |callback| unsafe {
138 com1.BeginUnregisterServiceNotificationFilter(
139 filterid,
140 timeout_milliseconds,
141 callback,
142 )
143 },
144 move |ctx| unsafe { com2.EndUnregisterServiceNotificationFilter(ctx) },
145 cancellation_token,
146 )
147 }
148
149 fn create_service_internal(
150 &self,
151 desc: &FABRIC_SERVICE_DESCRIPTION,
152 timeout_milliseconds: u32,
153 cancellation_token: Option<BoxedCancelToken>,
154 ) -> FabricReceiver<crate::WinResult<()>> {
155 let com1 = &self.com;
156 let com2 = self.com.clone();
157 fabric_begin_end_proxy(
158 move |callback| unsafe {
159 com1.BeginCreateService(desc, timeout_milliseconds, callback)
160 },
161 move |ctx| unsafe { com2.EndCreateService(ctx) },
162 cancellation_token,
163 )
164 }
165
166 fn update_service_internal(
167 &self,
168 name: FABRIC_URI,
169 desc: &FABRIC_SERVICE_UPDATE_DESCRIPTION,
170 timeout_milliseconds: u32,
171 cancellation_token: Option<BoxedCancelToken>,
172 ) -> FabricReceiver<crate::WinResult<()>> {
173 let com1 = &self.com;
174 let com2 = self.com.clone();
175 fabric_begin_end_proxy(
176 move |callback| unsafe {
177 com1.BeginUpdateService(name, desc, timeout_milliseconds, callback)
178 },
179 move |ctx| unsafe { com2.EndUpdateService(ctx) },
180 cancellation_token,
181 )
182 }
183
184 fn delete_service_internal(
185 &self,
186 name: FABRIC_URI,
187 timeout_milliseconds: u32,
188 cancellation_token: Option<BoxedCancelToken>,
189 ) -> FabricReceiver<crate::WinResult<()>> {
190 let com1 = &self.com;
191 let com2 = self.com.clone();
192 fabric_begin_end_proxy(
193 move |callback| unsafe {
194 com1.BeginDeleteService(name, timeout_milliseconds, callback)
195 },
196 move |ctx| unsafe { com2.EndDeleteService(ctx) },
197 cancellation_token,
198 )
199 }
200}
201
202impl From<IFabricServiceManagementClient6> for ServiceManagementClient {
203 fn from(com: IFabricServiceManagementClient6) -> Self {
204 Self { com }
205 }
206}
207
208impl From<ServiceManagementClient> for IFabricServiceManagementClient6 {
209 fn from(value: ServiceManagementClient) -> Self {
210 value.com
211 }
212}
213
214impl ServiceManagementClient {
217 pub async fn resolve_service_partition(
219 &self,
220 name: &Uri,
221 key_type: &PartitionKeyType,
222 prev: Option<&ResolvedServicePartition>,
223 timeout: Duration,
224 cancellation_token: Option<BoxedCancelToken>,
225 ) -> crate::Result<ResolvedServicePartition> {
226 let com = {
227 let uri = name.as_raw();
228 let prev_opt = prev.map(|x| &x.com);
230
231 let part_key_opt = key_type.get_raw_opt();
232
233 self.resolve_service_partition_internal(
234 uri,
235 key_type.into(),
236 part_key_opt,
237 prev_opt,
238 timeout.as_millis().try_into().unwrap(),
239 cancellation_token,
240 )
241 }
242 .await??;
243 let res = ResolvedServicePartition::from(com);
244 Ok(res)
245 }
246
247 pub async fn restart_replica(
252 &self,
253 desc: &RestartReplicaDescription,
254 timeout: Duration,
255 cancellation_token: Option<BoxedCancelToken>,
256 ) -> crate::Result<()> {
257 {
258 let raw: FABRIC_RESTART_REPLICA_DESCRIPTION = desc.into();
259 self.restart_replica_internal(&raw, timeout.as_millis() as u32, cancellation_token)
260 }
261 .await?
262 .map_err(crate::Error::from)
263 }
264
265 pub async fn remove_replica(
271 &self,
272 desc: &RemoveReplicaDescription,
273 timeout: Duration,
274 cancellation_token: Option<BoxedCancelToken>,
275 ) -> crate::Result<()> {
276 {
277 let raw: FABRIC_REMOVE_REPLICA_DESCRIPTION = desc.into();
278 self.remove_replica_internal(&raw, timeout.as_millis() as u32, cancellation_token)
279 }
280 .await?
281 .map_err(crate::Error::from)
282 }
283
284 pub async fn register_service_notification_filter(
299 &self,
300 desc: &ServiceNotificationFilterDescription,
301 timeout: Duration,
302 cancellation_token: Option<BoxedCancelToken>,
303 ) -> crate::Result<FilterIdHandle> {
304 let id = {
305 let raw: FABRIC_SERVICE_NOTIFICATION_FILTER_DESCRIPTION = desc.into();
306 self.register_service_notification_filter_internal(
307 &raw,
308 timeout.as_millis() as u32,
309 cancellation_token,
310 )
311 }
312 .await??;
313 Ok(FilterIdHandle { id })
314 }
315
316 pub async fn unregister_service_notification_filter(
320 &self,
321 filter_id_handle: FilterIdHandle,
322 timeout: Duration,
323 cancellation_token: Option<BoxedCancelToken>,
324 ) -> crate::Result<()> {
325 self.unregister_service_notification_filter_internal(
326 filter_id_handle.id,
327 timeout.as_millis() as u32,
328 cancellation_token,
329 )
330 .await?
331 .map_err(crate::Error::from)
332 }
333
334 pub async fn create_service(
335 &self,
336 desc: &crate::types::ServiceDescription,
337 timeout: Duration,
338 cancellation_token: Option<BoxedCancelToken>,
339 ) -> crate::Result<()> {
340 {
341 let desc_raw = desc.build_raw();
342 let ffi_raw = desc_raw.as_ffi();
343 self.create_service_internal(&ffi_raw, timeout.as_millis() as u32, cancellation_token)
344 }
345 .await?
346 .map_err(crate::Error::from)
347 }
348
349 pub async fn update_service(
350 &self,
351 name: &Uri,
352 desc: &crate::types::ServiceUpdateDescription,
353 timeout: Duration,
354 cancellation_token: Option<BoxedCancelToken>,
355 ) -> crate::Result<()> {
356 {
357 let desc_raw = desc.build_raw();
358 let ffi_raw = desc_raw.as_ffi();
359 self.update_service_internal(
360 name.as_raw(),
361 &ffi_raw,
362 timeout.as_millis() as u32,
363 cancellation_token,
364 )
365 }
366 .await?
367 .map_err(crate::Error::from)
368 }
369
370 pub async fn delete_service(
371 &self,
372 name: &Uri,
373 timeout: Duration,
374 cancellation_token: Option<BoxedCancelToken>,
375 ) -> crate::Result<()> {
376 self.delete_service_internal(
377 name.as_raw(),
378 timeout.as_millis() as u32,
379 cancellation_token,
380 )
381 .await?
382 .map_err(crate::Error::from)
383 }
384}
385
386#[derive(Debug, PartialEq)]
389pub struct FilterIdHandle {
390 pub(crate) id: i64,
391}
392
393#[derive(Debug, PartialEq)]
395pub enum PartitionKeyType {
396 Int64(i64),
397 Invalid,
398 None,
399 String(WString),
400}
401
402impl PartitionKeyType {
403 fn from_raw_svc_part(svc: ServicePartitionKind, data: *const c_void) -> PartitionKeyType {
404 match svc {
405 ServicePartitionKind::Int64Range => {
406 let x = data as *mut i64;
407 assert!(!x.is_null());
408 PartitionKeyType::Int64(unsafe { *x })
409 }
410 ServicePartitionKind::Invalid => PartitionKeyType::Invalid,
411 ServicePartitionKind::Singleton => PartitionKeyType::None,
412 ServicePartitionKind::Named => {
413 let x = data as *mut u16;
414 assert!(!x.is_null());
415 let s = WStringWrap::from(PCWSTR::from_raw(x)).into();
416 PartitionKeyType::String(s)
417 }
418 }
419 }
420}
421
422impl From<&PartitionKeyType> for FABRIC_PARTITION_KEY_TYPE {
423 fn from(value: &PartitionKeyType) -> Self {
424 match value {
425 PartitionKeyType::Int64(_) => FABRIC_PARTITION_KEY_TYPE_INT64,
426 PartitionKeyType::Invalid => FABRIC_PARTITION_KEY_TYPE_INVALID,
427 PartitionKeyType::None => FABRIC_PARTITION_KEY_TYPE_NONE,
428 PartitionKeyType::String(_) => FABRIC_PARTITION_KEY_TYPE_STRING,
429 }
430 }
431}
432
433impl PartitionKeyType {
434 fn get_raw_opt(&self) -> Option<*const c_void> {
436 match self {
437 PartitionKeyType::Int64(x) => Some(x as *const i64 as *const c_void),
439 PartitionKeyType::Invalid => None,
440 PartitionKeyType::None => None,
441 PartitionKeyType::String(x) => {
442 Some(PCWSTR::from_raw(x.as_ptr()).as_ptr() as *const c_void)
443 }
444 }
445 }
446}
447
448#[derive(Clone, Copy, Debug, PartialEq)]
449pub enum ServicePartitionKind {
450 Int64Range,
451 Invalid,
452 Named,
453 Singleton,
454}
455
456impl From<&ServicePartitionKind> for FABRIC_SERVICE_PARTITION_KIND {
457 fn from(value: &ServicePartitionKind) -> Self {
458 match value {
459 ServicePartitionKind::Int64Range => FABRIC_SERVICE_PARTITION_KIND_INT64_RANGE,
460 ServicePartitionKind::Invalid => FABRIC_SERVICE_PARTITION_KIND_INVALID,
461 ServicePartitionKind::Named => FABRIC_SERVICE_PARTITION_KIND_NAMED,
462 ServicePartitionKind::Singleton => FABRIC_SERVICE_PARTITION_KIND_SINGLETON,
463 }
464 }
465}
466
467impl From<FABRIC_SERVICE_PARTITION_KIND> for ServicePartitionKind {
468 fn from(value: FABRIC_SERVICE_PARTITION_KIND) -> Self {
469 match value {
470 FABRIC_SERVICE_PARTITION_KIND_INT64_RANGE => ServicePartitionKind::Int64Range,
471 FABRIC_SERVICE_PARTITION_KIND_INVALID => ServicePartitionKind::Invalid,
472 FABRIC_SERVICE_PARTITION_KIND_NAMED => ServicePartitionKind::Named,
473 FABRIC_SERVICE_PARTITION_KIND_SINGLETON => ServicePartitionKind::Singleton,
474 _ => {
475 if cfg!(debug_assertions) {
476 panic!("unknown type: {value:?}");
477 } else {
478 ServicePartitionKind::Invalid
479 }
480 }
481 }
482 }
483}
484
485#[derive(Debug, Clone)]
486pub struct ResolvedServicePartition {
487 com: IFabricResolvedServicePartitionResult,
488}
489
490impl From<IFabricResolvedServicePartitionResult> for ResolvedServicePartition {
491 fn from(com: IFabricResolvedServicePartitionResult) -> Self {
492 Self { com }
493 }
494}
495
496#[derive(Debug)]
497pub struct ResolvedServicePartitionInfo {
498 pub service_name: Uri,
499 pub service_partition_kind: ServicePartitionKind,
500 pub partition_key_type: PartitionKeyType,
501}
502
503impl ResolvedServicePartition {
504 pub fn get_info(&self) -> ResolvedServicePartitionInfo {
506 let raw = unsafe { self.com.get_Partition().as_ref().unwrap() };
507 let service_name = Uri::from(raw.ServiceName);
508 let kind_raw = raw.Info.Kind;
509 let val = raw.Info.Value;
510 let service_partition_kind: ServicePartitionKind = kind_raw.into();
511 let partition_key_type = PartitionKeyType::from_raw_svc_part(service_partition_kind, val);
512 ResolvedServicePartitionInfo {
513 service_name,
514 service_partition_kind,
515 partition_key_type,
516 }
517 }
518
519 pub fn get_endpoint_list(&self) -> ResolvedServiceEndpointList {
521 ResolvedServiceEndpointList::from(self.com.clone())
522 }
523
524 pub fn compare_version(&self, other: &ResolvedServicePartition) -> crate::Result<i32> {
529 unsafe { self.com.CompareVersion(&other.com) }.map_err(crate::Error::from)
530 }
531}
532
533impl PartialEq for ResolvedServicePartition {
534 fn eq(&self, other: &Self) -> bool {
535 match self.compare_version(other) {
536 Ok(i) => i == 0,
537 Err(_) => false, }
539 }
540}
541
542impl PartialOrd for ResolvedServicePartition {
543 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
547 match self.compare_version(other) {
548 Ok(i) => Some(i.cmp(&0)),
549 Err(_) => None,
551 }
552 }
553}
554
555#[derive(Debug, PartialEq, Clone)]
556pub enum ServiceEndpointRole {
557 Invalid,
558 StatefulPrimary,
559 StatefulSecondary,
560 Stateless,
561}
562
563impl From<FABRIC_SERVICE_ENDPOINT_ROLE> for ServiceEndpointRole {
564 fn from(value: FABRIC_SERVICE_ENDPOINT_ROLE) -> Self {
565 match value {
566 FABRIC_SERVICE_ROLE_INVALID => ServiceEndpointRole::Invalid,
567 FABRIC_SERVICE_ROLE_STATEFUL_PRIMARY => ServiceEndpointRole::StatefulPrimary,
568 FABRIC_SERVICE_ROLE_STATEFUL_SECONDARY => ServiceEndpointRole::StatefulSecondary,
569 FABRIC_SERVICE_ROLE_STATELESS => ServiceEndpointRole::Stateless,
570 _ => {
571 if cfg!(debug_assertions) {
572 panic!("unknown type: {value:?}");
573 } else {
574 ServiceEndpointRole::Invalid
575 }
576 }
577 }
578 }
579}
580
581pub struct ResolvedServiceEndpointList {
582 com: IFabricResolvedServicePartitionResult,
583}
584
585impl From<IFabricResolvedServicePartitionResult> for ResolvedServiceEndpointList {
586 fn from(com: IFabricResolvedServicePartitionResult) -> Self {
587 Self { com }
588 }
589}
590
591impl ResolvedServiceEndpointList {
592 pub fn iter(&self) -> ResolvedServiceEndpointListIter<'_> {
594 ResolvedServiceEndpointListIter::new(self, self)
595 }
596}
597
598impl FabricListAccessor<FABRIC_RESOLVED_SERVICE_ENDPOINT> for ResolvedServiceEndpointList {
599 fn get_count(&self) -> u32 {
600 let raw = unsafe { self.com.get_Partition().as_ref().unwrap() };
601 raw.EndpointCount
602 }
603
604 fn get_first_item(&self) -> *const FABRIC_RESOLVED_SERVICE_ENDPOINT {
605 let raw = unsafe { self.com.get_Partition().as_ref().unwrap() };
606 raw.Endpoints
607 }
608}
609
610#[derive(Debug, Clone, PartialEq)]
611pub struct ResolvedServiceEndpoint {
612 pub address: WString,
613 pub role: ServiceEndpointRole,
614}
615
616type ResolvedServiceEndpointListIter<'a> = FabricIter<
617 'a,
618 FABRIC_RESOLVED_SERVICE_ENDPOINT,
619 ResolvedServiceEndpoint,
620 ResolvedServiceEndpointList,
621>;
622
623impl From<&FABRIC_RESOLVED_SERVICE_ENDPOINT> for ResolvedServiceEndpoint {
624 fn from(value: &FABRIC_RESOLVED_SERVICE_ENDPOINT) -> Self {
625 let raw = value;
626 Self {
627 address: WStringWrap::from(raw.Address).into(),
628 role: raw.Role.into(),
629 }
630 }
631}
632
633#[cfg(test)]
634mod tests {
635 use crate::{PCWSTR, WString};
636
637 use super::{PartitionKeyType, ServicePartitionKind};
638
639 #[test]
640 fn test_conversion_int() {
641 let k = PartitionKeyType::Int64(99);
642 let raw = k.get_raw_opt();
644 let i = unsafe { (raw.unwrap() as *const i64).as_ref().unwrap() };
645 assert_eq!(*i, 99);
646
647 let service_type = ServicePartitionKind::Int64Range;
648 let k2 = PartitionKeyType::from_raw_svc_part(service_type, raw.unwrap());
650 assert_eq!(k, k2);
651 }
652
653 #[test]
654 fn test_conversion_string() {
655 let src = WString::from("mystr");
656 let k = PartitionKeyType::String(src.clone());
657 let raw = k.get_raw_opt();
659 let s = WString::from(PCWSTR::from_raw(raw.unwrap() as *const u16));
660 assert_eq!(s, src);
661
662 let service_type = ServicePartitionKind::Named;
663 let k2 = PartitionKeyType::from_raw_svc_part(service_type, raw.unwrap());
665 assert_eq!(k, k2);
666 }
667}