1use crate::address::{RemoteEndpoint, RemoteFile, RemoteLocation};
4use crate::pool::{EndpointHandle, Job, Registry, Reply, Request, SessionState};
5use crate::{ReadFailureKind, ReadSelection, ReadStage, RemoteReadError, RemoteWindow};
6use std::sync::{mpsc, Arc};
7use std::time::Duration;
8use strop_core::worker::CancelToken;
9
10const CANCEL_POLL: Duration = Duration::from_millis(50);
11
12#[derive(Debug, Clone)]
13pub struct ConnectionLease {
14 pub(crate) inner: Arc<EndpointHandle>,
15}
16impl ConnectionLease {
17 pub fn endpoint(&self) -> &RemoteEndpoint {
18 self.inner.endpoint()
19 }
20 pub fn is_connected(&self) -> bool {
21 !self.inner.stop_signal().signalled() && self.inner.status() == SessionState::Connected
22 }
23}
24
25pub struct RemoteSnapshot {
26 pub file: RemoteFile,
27 pub buffer: strop_core::Buffer,
28 pub window: RemoteWindow,
29 pub connection: ConnectionLease,
30}
31pub struct RemoteDirectorySnapshot {
32 pub directory: RemoteFile,
33 pub entries: Vec<RemoteEntry>,
34 pub connection: ConnectionLease,
35}
36pub enum RemoteResource {
37 File(Box<RemoteSnapshot>),
38 Directory(RemoteDirectorySnapshot),
39}
40#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
41pub struct RemoteEntry {
42 pub file: RemoteFile,
43 pub kind: RemoteEntryKind,
44}
45#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
46pub enum RemoteEntryKind {
47 File,
48 Directory,
49 Other,
50}
51
52#[derive(Clone, Default)]
53pub struct RemoteClient {
54 registry: Arc<Registry>,
55}
56impl RemoteClient {
57 pub fn new() -> Self {
58 Self::default()
59 }
60
61 pub fn read(
62 &self,
63 location: &RemoteLocation,
64 selection: ReadSelection,
65 token: &CancelToken,
66 ) -> Result<RemoteSnapshot, RemoteReadError> {
67 match self.request(
68 location.endpoint(),
69 Request::Read {
70 location: location.clone(),
71 selection,
72 },
73 token,
74 false,
75 )? {
76 Reply::Snapshot(snapshot) => Ok(*snapshot),
77 _ => Err(wrong_reply()),
78 }
79 }
80 pub fn open(
81 &self,
82 location: &RemoteLocation,
83 selection: ReadSelection,
84 token: &CancelToken,
85 ) -> Result<RemoteResource, RemoteReadError> {
86 match self.request(
87 location.endpoint(),
88 Request::Open {
89 location: location.clone(),
90 selection,
91 },
92 token,
93 false,
94 )? {
95 Reply::Snapshot(snapshot) => Ok(RemoteResource::File(snapshot)),
96 Reply::Directory(snapshot) => Ok(RemoteResource::Directory(snapshot)),
97 _ => Err(wrong_reply()),
98 }
99 }
100 pub fn list(
101 &self,
102 location: &RemoteLocation,
103 token: &CancelToken,
104 ) -> Result<RemoteDirectorySnapshot, RemoteReadError> {
105 match self.request(
106 location.endpoint(),
107 Request::List {
108 location: location.clone(),
109 },
110 token,
111 false,
112 )? {
113 Reply::Directory(snapshot) => Ok(snapshot),
114 _ => Err(wrong_reply()),
115 }
116 }
117 pub fn list_connected(
120 &self,
121 directory: &RemoteFile,
122 token: &CancelToken,
123 ) -> Result<Vec<RemoteEntry>, RemoteReadError> {
124 match self.request(
125 directory.endpoint(),
126 Request::ListConnected {
127 directory: directory.clone(),
128 },
129 token,
130 true,
131 )? {
132 Reply::Entries(entries) => Ok(entries),
133 _ => Err(wrong_reply()),
134 }
135 }
136 pub fn connect(
137 &self,
138 endpoint: &RemoteEndpoint,
139 token: &CancelToken,
140 ) -> Result<ConnectionLease, RemoteReadError> {
141 match self.request(endpoint, Request::Connect, token, false)? {
142 Reply::Lease(lease) => Ok(lease),
143 _ => Err(wrong_reply()),
144 }
145 }
146 pub fn disconnect(&self, endpoint: &RemoteEndpoint) -> Result<(), RemoteReadError> {
148 if let Some(handle) = self.registry.remove(endpoint) {
149 handle.wait_stopped()?;
150 }
151 Ok(())
152 }
153 pub fn disconnect_all(&self) -> Result<(), RemoteReadError> {
154 let handles = self.registry.clear();
155 let mut failure = None;
156 for handle in handles {
157 if let Err(error) = handle.wait_stopped() {
158 failure.get_or_insert(error);
159 }
160 }
161 failure.map_or(Ok(()), Err)
162 }
163 pub fn connections(&self) -> Vec<RemoteEndpoint> {
165 self.registry.connected()
166 }
167
168 fn request(
169 &self,
170 endpoint: &RemoteEndpoint,
171 request: Request,
172 cancel: &CancelToken,
173 connected_only: bool,
174 ) -> Result<Reply, RemoteReadError> {
175 if cancel.is_cancelled() {
176 return Err(cancelled());
177 }
178 let inner = if connected_only {
179 self.registry.connected_handle(endpoint).ok_or_else(|| {
180 RemoteReadError::bare(
181 ReadStage::Session,
182 ReadFailureKind::NotConnected,
183 "completion requires an existing authenticated connection",
184 )
185 })?
186 } else {
187 self.registry.acquire(endpoint)?
188 };
189 let (reply, receiver) = mpsc::channel();
190 let job = Job {
191 lease: ConnectionLease {
192 inner: inner.clone(),
193 },
194 request,
195 cancel: cancel.clone(),
196 reply,
197 };
198 inner.jobs().try_send(job).map_err(|error| match error {
199 mpsc::TrySendError::Full(_) => RemoteReadError::bare(
200 ReadStage::Session,
201 ReadFailureKind::QueueFull,
202 "remote session queue is full",
203 ),
204 mpsc::TrySendError::Disconnected(_) => stopped(),
205 })?;
206 loop {
207 if cancel.is_cancelled() {
208 return Err(cancelled());
209 }
210 if inner.stop_signal().signalled() {
211 return Err(stopped());
212 }
213 match receiver.recv_timeout(CANCEL_POLL) {
214 Ok(result) => return result,
215 Err(mpsc::RecvTimeoutError::Timeout) => {}
216 Err(mpsc::RecvTimeoutError::Disconnected) => return Err(stopped()),
217 }
218 }
219 }
220}
221fn cancelled() -> RemoteReadError {
222 RemoteReadError::bare(
223 ReadStage::Session,
224 ReadFailureKind::Cancelled,
225 "remote request cancelled",
226 )
227}
228fn stopped() -> RemoteReadError {
229 RemoteReadError::bare(
230 ReadStage::Session,
231 ReadFailureKind::Stopped,
232 "remote session stopped before delivering a result",
233 )
234}
235fn wrong_reply() -> RemoteReadError {
236 RemoteReadError::bare(
237 ReadStage::Session,
238 ReadFailureKind::Protocol,
239 "session reply does not match the accepted request",
240 )
241}