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