lzc_sdk/
service_status.rs1use std::collections::{HashMap, HashSet};
2use std::fmt;
3use std::sync::Arc;
4
5use tonic::Code;
6use tonic::transport::Channel;
7
8use crate::proto::common::file_handler_server;
9use crate::proto::localdevice::device_service_client::DeviceServiceClient;
10use crate::proto::localdevice::photo_library_server;
11use crate::proto::localdevice::{LocalServiceState, LocalServiceStatus, QueryServiceStatusRequest};
12use crate::{AuthenticatedService, Error};
13
14type DeviceServiceTransport = AuthenticatedService<Channel>;
15
16#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
18pub enum ServiceState {
19 #[default]
20 Unknown,
21 Available,
22 Unavailable,
23}
24
25impl ServiceState {
26 #[must_use]
28 pub const fn as_str(self) -> &'static str {
29 match self {
30 Self::Unknown => "unknown",
31 Self::Available => "available",
32 Self::Unavailable => "unavailable",
33 }
34 }
35}
36
37impl fmt::Display for ServiceState {
38 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
39 formatter.write_str(self.as_str())
40 }
41}
42
43#[derive(Clone, Debug, Default, Eq, PartialEq)]
45pub struct ServiceStatus {
46 pub state: ServiceState,
47 pub reason: String,
48}
49
50#[derive(Clone, Debug)]
52pub struct ServiceStatusQuerier {
53 registry: ServiceStatusRegistry,
54 service_name: Arc<str>,
55}
56
57impl ServiceStatusQuerier {
58 fn new(registry: ServiceStatusRegistry, service_name: &'static str) -> Self {
59 Self {
60 registry,
61 service_name: service_name.into(),
62 }
63 }
64
65 pub async fn query(&self) -> Result<ServiceStatus, Error> {
71 self.registry.query(self.service_name.as_ref()).await
72 }
73}
74
75#[derive(Clone, Debug)]
77pub struct ServiceStatusRegistry {
78 service: DeviceServiceTransport,
79}
80
81impl ServiceStatusRegistry {
82 pub(crate) fn new(service: DeviceServiceTransport) -> Self {
83 Self { service }
84 }
85
86 pub async fn query(&self, service_name: &str) -> Result<ServiceStatus, Error> {
92 let mut statuses = self.query_many([service_name]).await?;
93 Ok(statuses.remove(service_name).unwrap_or_default())
94 }
95
96 pub async fn query_many<I, S>(
102 &self,
103 service_names: I,
104 ) -> Result<HashMap<String, ServiceStatus>, Error>
105 where
106 I: IntoIterator<Item = S>,
107 S: AsRef<str>,
108 {
109 let mut seen = HashSet::new();
110 let service_name = service_names
111 .into_iter()
112 .filter_map(|name| {
113 let name = name.as_ref();
114 if seen.insert(name.to_owned()) {
115 Some(name.to_owned())
116 } else {
117 None
118 }
119 })
120 .collect();
121 let response = DeviceServiceClient::new(self.service.clone())
122 .query_service_status(QueryServiceStatusRequest { service_name })
123 .await
124 .map_err(|status| {
125 if status.code() == Code::Unimplemented {
126 Error::ServiceStatusUnsupported
127 } else {
128 Error::GrpcStatus(status)
129 }
130 })?
131 .into_inner();
132 Ok(response
133 .services
134 .into_iter()
135 .map(|(name, status)| (name, convert_status(status)))
136 .collect())
137 }
138}
139
140fn convert_status(status: LocalServiceStatus) -> ServiceStatus {
141 let state = match LocalServiceState::try_from(status.state).unwrap_or_default() {
142 LocalServiceState::Available => ServiceState::Available,
143 LocalServiceState::Unavailable => ServiceState::Unavailable,
144 LocalServiceState::Unknown => ServiceState::Unknown,
145 };
146 ServiceStatus {
147 state,
148 reason: status.reason,
149 }
150}
151
152#[derive(Clone, Debug)]
154pub struct DeviceProxyStatus {
155 photo_library: ServiceStatusQuerier,
156 file_handler: ServiceStatusQuerier,
157 services: ServiceStatusRegistry,
158}
159
160impl DeviceProxyStatus {
161 pub(crate) fn new(service: DeviceServiceTransport) -> Self {
162 let services = ServiceStatusRegistry::new(service);
163 Self {
164 photo_library: ServiceStatusQuerier::new(
165 services.clone(),
166 photo_library_server::SERVICE_NAME,
167 ),
168 file_handler: ServiceStatusQuerier::new(
169 services.clone(),
170 file_handler_server::SERVICE_NAME,
171 ),
172 services,
173 }
174 }
175
176 #[must_use]
177 pub fn photo_library(&self) -> ServiceStatusQuerier {
178 self.photo_library.clone()
179 }
180
181 #[must_use]
182 pub fn file_handler(&self) -> ServiceStatusQuerier {
183 self.file_handler.clone()
184 }
185
186 #[must_use]
187 pub fn services(&self) -> ServiceStatusRegistry {
188 self.services.clone()
189 }
190}