darkbio_wire/protocol/closer.rs
1// wire-rs: encrypted protocol between Ark and host
2// Copyright 2026 Dark Bio AG. All rights reserved.
3//
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file.
6
7//! Handles for closing a session or server from another thread.
8
9use super::{Error, server::ServerInner, session::SessionInner};
10use std::sync::Weak;
11
12/// Clonable handle for closing the session or server that created it.
13///
14/// From [`super::Session::closer`], it closes that session, with the semantics of
15/// [`super::Session::close`]. From [`super::Server::closer`], it closes the server
16/// and its active session, with the semantics of [`super::Server::close`].
17/// Its target never changes: a session's closer cannot affect a successor session.
18///
19/// This handle does not keep its owner open. Dropping it does not close anything.
20#[derive(Clone, Debug)]
21pub struct Closer {
22 /// Session or server to close.
23 target: Target,
24}
25
26/// The session or server targeted by a `Closer`.
27#[derive(Clone, Debug)]
28enum Target {
29 /// One session, even after another session connects to the same server.
30 Session(Weak<SessionInner>),
31 /// One persistent server and whichever session it has attached at closure.
32 Server(Weak<ServerInner>),
33}
34
35impl Closer {
36 /// Closes the original owner. Repeated calls have no further effect.
37 pub fn close(&self) {
38 match &self.target {
39 Target::Session(target) => {
40 if let Some(session) = target.upgrade() {
41 session.close(Error::Closed);
42 }
43 }
44 Target::Server(target) => {
45 if let Some(server) = target.upgrade() {
46 server.close(Error::Closed);
47 }
48 }
49 }
50 }
51
52 /// Creates a closer for one session using a weak reference.
53 pub(super) fn session(target: Weak<SessionInner>) -> Self {
54 Self {
55 target: Target::Session(target),
56 }
57 }
58
59 /// Creates a closer for one server using a weak reference.
60 pub(super) fn server(target: Weak<ServerInner>) -> Self {
61 Self {
62 target: Target::Server(target),
63 }
64 }
65}
66
67/// Checks that the same closer type works for sessions and servers.
68#[cfg(test)]
69#[cfg_attr(coverage_nightly, coverage(off))]
70mod tests {
71 use crate::protocol::{Closer, Server, Session};
72 use std::fmt::Debug;
73
74 /// Compiles closing sessions and servers through cloned handles on other threads.
75 #[allow(dead_code)]
76 fn cross_thread_close(session: &Session, server: &Server) {
77 let session_closer: Closer = session.closer();
78 let server_closer: Closer = server.closer();
79 let session_copy = session_closer.clone();
80 let server_copy = server_closer.clone();
81 std::thread::spawn(move || session_copy.close());
82 std::thread::spawn(move || server_copy.close());
83 session.close();
84 server.close();
85 }
86
87 /// Checks that `Closer` implements `Clone`, `Debug`, `Send`, and `Sync`.
88 #[test]
89 fn test_thread_capabilities() {
90 /// Requires a handle to be clonable, printable and usable by multiple threads.
91 fn shared<T: Clone + Debug + Send + Sync + 'static>() {}
92 shared::<Closer>();
93 }
94}