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, so a session's closer cannot affect a successor
18/// session.
19///
20/// This handle does not keep its owner open. Dropping it does not close anything.
21#[derive(Clone, Debug)]
22pub struct Closer {
23 /// Session or server to close.
24 target: Target,
25}
26
27/// Session or server targeted by a [`Closer`].
28#[derive(Clone, Debug)]
29enum Target {
30 /// One session, even after another session connects to the same server.
31 Session(Weak<SessionInner>),
32 /// One persistent server and whichever session it has attached at closure.
33 Server(Weak<ServerInner>),
34}
35
36impl Closer {
37 /// Closes the session or server that created this handle.
38 ///
39 /// Repeated calls have no further effect.
40 pub fn close(&self) {
41 match &self.target {
42 Target::Session(target) => {
43 if let Some(session) = target.upgrade() {
44 session.close(Error::Closed);
45 }
46 }
47 Target::Server(target) => {
48 if let Some(server) = target.upgrade() {
49 server.close(Error::Closed);
50 }
51 }
52 }
53 }
54
55 /// Creates a closer targeting one session without keeping it alive.
56 pub(super) fn session(target: Weak<SessionInner>) -> Self {
57 Self {
58 target: Target::Session(target),
59 }
60 }
61
62 /// Creates a closer targeting one server without keeping it alive.
63 pub(super) fn server(target: Weak<ServerInner>) -> Self {
64 Self {
65 target: Target::Server(target),
66 }
67 }
68}
69
70/// Checks that the same closer type works for sessions and servers.
71#[cfg(test)]
72#[cfg_attr(coverage_nightly, coverage(off))]
73mod tests {
74 use crate::protocol::{Closer, Server, Session};
75 use std::fmt::Debug;
76
77 /// Compiles closing sessions and servers through cloned handles on other threads.
78 #[allow(dead_code)]
79 fn cross_thread_close(session: &Session, server: &Server) {
80 let session_closer: Closer = session.closer();
81 let server_closer: Closer = server.closer();
82 let session_copy = session_closer.clone();
83 let server_copy = server_closer.clone();
84 std::thread::spawn(move || session_copy.close());
85 std::thread::spawn(move || server_copy.close());
86 session.close();
87 server.close();
88 }
89
90 /// Checks that `Closer` implements `Clone`, `Debug`, `Send`, and `Sync`.
91 #[test]
92 fn test_thread_capabilities() {
93 /// Requires a handle to be clonable, printable and usable by multiple threads.
94 fn shared<T: Clone + Debug + Send + Sync + 'static>() {}
95 shared::<Closer>();
96 }
97}