openlogi_device/
channel.rs1use std::sync::{Arc, Weak};
12
13use hidpp::channel::HidppChannel;
14
15use route::DeviceRoute;
16
17pub(crate) mod pool;
18pub(crate) mod registry;
19pub(crate) mod route;
20#[cfg(test)]
21pub(crate) mod scripted;
22
23pub use pool::ChannelPool;
24pub use registry::ChannelRegistry;
25
26#[derive(Clone)]
33pub struct SharedChannel {
34 channel: Arc<HidppChannel>,
35 route: DeviceRoute,
36}
37
38impl SharedChannel {
39 #[must_use]
41 pub(crate) fn new(channel: Arc<HidppChannel>, route: DeviceRoute) -> Self {
42 Self { channel, route }
43 }
44
45 #[must_use]
48 pub fn matches(&self, route: &DeviceRoute) -> bool {
49 self.route == *route
50 }
51
52 pub(crate) fn channel(&self) -> &Arc<HidppChannel> {
53 &self.channel
54 }
55
56 pub(crate) fn device_index(&self) -> u8 {
57 self.route.device_index()
58 }
59
60 #[must_use]
63 pub fn route(&self) -> &DeviceRoute {
64 &self.route
65 }
66
67 #[must_use]
70 pub fn downgrade(&self) -> WeakSharedChannel {
71 WeakSharedChannel {
72 channel: Arc::downgrade(&self.channel),
73 route: self.route.clone(),
74 }
75 }
76}
77
78#[derive(Clone)]
82pub struct WeakSharedChannel {
83 channel: Weak<HidppChannel>,
84 route: DeviceRoute,
85}
86
87impl WeakSharedChannel {
88 #[must_use]
90 pub fn upgrade(&self) -> Option<SharedChannel> {
91 self.channel
92 .upgrade()
93 .map(|channel| SharedChannel::new(channel, self.route.clone()))
94 }
95}
96
97impl PartialEq for WeakSharedChannel {
98 fn eq(&self, other: &Self) -> bool {
99 self.route == other.route && Weak::ptr_eq(&self.channel, &other.channel)
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::scripted::{ScriptedRawHidChannel, scripted_channel};
106 use super::*;
107
108 fn route(slot: u8) -> DeviceRoute {
109 DeviceRoute::Unifying {
110 receiver_uid: "weak-channel".into(),
111 slot,
112 }
113 }
114
115 async fn connection() -> Arc<HidppChannel> {
116 let (raw, _handle) = ScriptedRawHidChannel::with_dynamic_responder(|_| None);
117 scripted_channel(raw).await
118 }
119
120 #[tokio::test]
121 async fn a_weak_channel_names_one_connection_without_keeping_it_open() {
122 let receiver = connection().await;
123 let shared = SharedChannel::new(Arc::clone(&receiver), route(1));
124 let weak = shared.downgrade();
125 assert!(weak == shared.downgrade());
126 assert!(
127 weak.upgrade()
128 .is_some_and(|channel| channel.matches(&route(1)))
129 );
130
131 let reconnected = SharedChannel::new(connection().await, route(1));
132 assert!(
133 weak != reconnected.downgrade(),
134 "a reconnect is another connection"
135 );
136 let other_slot = SharedChannel::new(Arc::clone(&receiver), route(2));
137 assert!(
138 weak != other_slot.downgrade(),
139 "the same receiver, another slot"
140 );
141
142 drop((receiver, shared, other_slot));
143 assert!(
144 weak.upgrade().is_none(),
145 "a closed connection is not reopened"
146 );
147 }
148}