up_rust/core/usubscription.rs
1/********************************************************************************
2 * Copyright (c) 2024 Contributors to the Eclipse Foundation
3 *
4 * See the NOTICE file(s) distributed with this work for additional
5 * information regarding copyright ownership.
6 *
7 * This program and the accompanying materials are made available under the
8 * terms of the Apache License Version 2.0 which is available at
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * SPDX-License-Identifier: Apache-2.0
12 ********************************************************************************/
13
14use async_trait::async_trait;
15use core::hash::{Hash, Hasher};
16#[cfg(test)]
17use mockall::automock;
18
19pub use crate::up_core_api::usubscription::{
20 fetch_subscriptions_request::Request, subscription_status::State, EventDeliveryConfig,
21 FetchSubscribersRequest, FetchSubscribersResponse, FetchSubscriptionsRequest,
22 FetchSubscriptionsResponse, NotificationsRequest, NotificationsResponse, SubscribeAttributes,
23 SubscriberInfo, Subscription, SubscriptionRequest, SubscriptionResponse, SubscriptionStatus,
24 UnsubscribeRequest, UnsubscribeResponse, Update,
25};
26
27use crate::{UStatus, UUri};
28
29impl Hash for SubscriberInfo {
30 /// Creates a hash value based on the URI property.
31 ///
32 /// # Examples
33 ///
34 /// ```rust
35 /// use std::hash::{DefaultHasher, Hash, Hasher};
36 /// use up_rust::UUri;
37 /// use up_rust::core::usubscription::SubscriberInfo;
38 ///
39 /// let mut hasher = DefaultHasher::new();
40 /// let info = SubscriberInfo {
41 /// uri: Some(UUri::try_from_parts("", 0x1000, 0x01, 0x9a00).unwrap()).into(),
42 /// ..Default::default()
43 /// };
44 ///
45 /// info.hash(&mut hasher);
46 /// let hash_one = hasher.finish();
47 ///
48 /// let mut hasher = DefaultHasher::new();
49 /// let info = SubscriberInfo {
50 /// uri: Some(UUri::try_from_parts("", 0x1000, 0x02, 0xf100).unwrap()).into(),
51 /// ..Default::default()
52 /// };
53 ///
54 /// info.hash(&mut hasher);
55 /// let hash_two = hasher.finish();
56 ///
57 /// assert_ne!(hash_one, hash_two);
58 /// ```
59 fn hash<H: Hasher>(&self, state: &mut H) {
60 self.uri.hash(state);
61 }
62}
63
64impl Eq for SubscriberInfo {}
65
66/// Checks if a given [`SubscriberInfo`] contains any information.
67///
68/// # Returns
69///
70/// `true` if the given instance is equal to [`SubscriberInfo::default`], `false` otherwise.
71///
72/// # Examples
73///
74/// ```rust
75/// use up_rust::UUri;
76/// use up_rust::core::usubscription::SubscriberInfo;
77///
78/// let mut info = SubscriberInfo::default();
79/// assert!(info.is_empty());
80///
81/// info.uri = Some(UUri::try_from_parts("", 0x1000, 0x01, 0x9a00).unwrap()).into();
82/// assert!(!info.is_empty());
83/// ```
84impl SubscriberInfo {
85 pub fn is_empty(&self) -> bool {
86 self.eq(&SubscriberInfo::default())
87 }
88}
89
90impl SubscriptionResponse {
91 /// Checks if this `SubscriptionResponse` is in a specific state (`usubscription::State``).
92 ///
93 /// Returns `true` if SubscriptionResponse contains a valid SubscriptionStatus, which has a
94 /// state property that is equal to state passed as argument.
95 ///
96 /// # Examples
97 ///
98 /// ```rust
99 /// use up_rust::core::usubscription::{SubscriptionResponse, SubscriptionStatus, State};
100 ///
101 /// let subscription_response = SubscriptionResponse {
102 /// status: Some(SubscriptionStatus {
103 /// state: State::SUBSCRIBED.into(),
104 /// ..Default::default()
105 /// }).into(),
106 /// ..Default::default()
107 /// };
108 /// assert!(subscription_response.is_state(State::SUBSCRIBED));
109 /// ```
110 pub fn is_state(&self, state: State) -> bool {
111 self.status
112 .as_ref()
113 .is_some_and(|ss| ss.state.enum_value().is_ok_and(|s| s.eq(&state)))
114 }
115}
116
117/// The uEntity (type) identifier of the uSubscription service.
118pub const USUBSCRIPTION_TYPE_ID: u32 = 0x0000_0000;
119/// The (latest) major version of the uSubscription service.
120pub const USUBSCRIPTION_VERSION_MAJOR: u8 = 0x03;
121/// The resource identifier of uSubscription's _subscribe_ operation.
122pub const RESOURCE_ID_SUBSCRIBE: u16 = 0x0001;
123/// The resource identifier of uSubscription's _unsubscribe_ operation.
124pub const RESOURCE_ID_UNSUBSCRIBE: u16 = 0x0002;
125/// The resource identifier of uSubscription's _fetch subscriptions_ operation.
126pub const RESOURCE_ID_FETCH_SUBSCRIPTIONS: u16 = 0x0003;
127/// The resource identifier of uSubscription's _register for notifications_ operation.
128pub const RESOURCE_ID_REGISTER_FOR_NOTIFICATIONS: u16 = 0x0006;
129/// The resource identifier of uSubscription's _unregister for notifications_ operation.
130pub const RESOURCE_ID_UNREGISTER_FOR_NOTIFICATIONS: u16 = 0x0007;
131/// The resource identifier of uSubscription's _fetch subscribers_ operation.
132pub const RESOURCE_ID_FETCH_SUBSCRIBERS: u16 = 0x0008;
133
134/// The resource identifier of uSubscription's _subscription change_ topic.
135pub const RESOURCE_ID_SUBSCRIPTION_CHANGE: u16 = 0x8000;
136
137/// Gets a UUri referring to one of the local uSubscription service's resources.
138///
139/// # Examples
140///
141/// ```rust
142/// use up_rust::core::usubscription;
143///
144/// let uuri = usubscription::usubscription_uri(usubscription::RESOURCE_ID_SUBSCRIBE);
145/// assert_eq!(uuri.resource_id, 0x0001);
146/// ```
147pub fn usubscription_uri(resource_id: u16) -> UUri {
148 UUri::try_from_parts(
149 "",
150 USUBSCRIPTION_TYPE_ID,
151 USUBSCRIPTION_VERSION_MAJOR,
152 resource_id,
153 )
154 .unwrap()
155}
156
157/// The uProtocol Application Layer client interface to the uSubscription service.
158///
159/// Please refer to the [uSubscription service specification](https://github.com/eclipse-uprotocol/up-spec/blob/main/up-l3/usubscription/v3/README.adoc)
160/// for details.
161#[cfg_attr(test, automock)]
162#[async_trait]
163pub trait USubscription: Send + Sync {
164 /// Subscribe to a topic, using a [`SubscriptionRequest`]
165 ///
166 /// # Parameters
167 ///
168 /// * `subscription_request` - A request to subscribe
169 ///
170 /// # Returns
171 ///
172 /// * [`SubscriptionResponse`] detailing if subscription was successful with other metadata
173 async fn subscribe(
174 &self,
175 subscription_request: SubscriptionRequest,
176 ) -> Result<SubscriptionResponse, UStatus>;
177
178 /// Unsubscribe to a topic, using an [`UnsubscribeRequest`]
179 ///
180 /// # Parameters
181 ///
182 /// * `unsubscribe_request` - A request to unsubscribe
183 ///
184 /// # Returns
185 ///
186 /// * [`UStatus`] detailing if unsubscription was successful and if not why not
187 async fn unsubscribe(&self, unsubscribe_request: UnsubscribeRequest) -> Result<(), UStatus>;
188
189 /// Fetch all subscriptions for a given topic or subscriber contained inside a [`FetchSubscriptionsRequest`]
190 ///
191 /// # Parameters
192 ///
193 /// * `fetch_subscriptions_request` - A request to fetch subscriptions given a topic or subscriber
194 ///
195 /// # Returns
196 ///
197 /// * [`FetchSubscriptionsResponse`] detailing the zero or more subscriptions' info
198 async fn fetch_subscriptions(
199 &self,
200 fetch_subscriptions_request: FetchSubscriptionsRequest,
201 ) -> Result<FetchSubscriptionsResponse, UStatus>;
202
203 /// Register for notifications relevant to a given topic inside a [`NotificationsRequest`]
204 /// changing in subscription status.
205 ///
206 /// # Parameters
207 ///
208 /// * `notifications_register_request` - A request to receive changes to subscription status
209 ///
210 /// # Returns
211 ///
212 /// * [`UStatus`] detailing if notification registration was successful and if not why not
213 async fn register_for_notifications(
214 &self,
215 notifications_register_request: NotificationsRequest,
216 ) -> Result<(), UStatus>;
217
218 /// Unregister for notifications relevant to a given topic inside a [`NotificationsRequest`]
219 /// changing in subscription status.
220 ///
221 /// # Parameters
222 ///
223 /// * `notifications_unregister_request` - A request to no longer receive changes to subscription status
224 ///
225 /// # Returns
226 ///
227 /// * [`UStatus`] detailing if notification unregistration was successful and if not why not
228 async fn unregister_for_notifications(
229 &self,
230 notifications_unregister_request: NotificationsRequest,
231 ) -> Result<(), UStatus>;
232
233 /// Fetch a list of subscribers that are currently subscribed to a given topic in a [`FetchSubscribersRequest`]
234 ///
235 /// # Parameters
236 ///
237 /// * `fetch_subscribers_request` - Request containing topic for which we'd like all subscribers' info
238 ///
239 /// # Returns
240 ///
241 /// * [`FetchSubscriptionsResponse`] detailing subscriber info for the provided topic
242 async fn fetch_subscribers(
243 &self,
244 fetch_subscribers_request: FetchSubscribersRequest,
245 ) -> Result<FetchSubscribersResponse, UStatus>;
246}