use crate::TopLevelError; use error_stack::ResultExt; use tokio::net::{UnixListener, UnixStream}; use tracing::Instrument; pub struct SocketServer { name: String, path: String, num_sessions_run: u64, listener: UnixListener, } #[allow(unused)] impl SocketServer { /// Create a new server that will handle inputs from the client program. /// /// This function will just generate the function required, without starting the /// underlying task. To start the task, use [`SocketServer::start`], although that /// method will take ownership of the object. pub fn new(name: String, listener: UnixListener) -> Self { let path = listener .local_addr() .map(|x| x.as_pathname().map(|p| format!("{}", p.display()))) .unwrap_or_else(|_| None) .unwrap_or_else(|| "".to_string()); tracing::trace!(%name, %path, "Creating new socket listener"); SocketServer { name, path, num_sessions_run: 0, listener, } } /// Start running the service, returning a handle that will pass on an error if /// one occurs in the core of this task. /// /// Typically, errors shouldn't happen in the core task, as all it does is listen /// for new connections and then spawn other tasks based on them. If errors occur /// there, the core task should be unaffected. pub async fn start(mut self) -> error_stack::Result<(), TopLevelError> { loop { let (stream, addr) = self .listener .accept() .await .change_context(TopLevelError::SocketHandlerFailure)?; let remote_addr = addr .as_pathname() .map(|x| x.display()) .map(|x| format!("{}", x)) .unwrap_or("".to_string()); let span = tracing::debug_span!( "unix socket handler", socket_name = %self.name, socket_path = %self.path, session_no = %self.num_sessions_run, %remote_addr, ); self.num_sessions_run += 1; tokio::task::spawn(Self::run_session(stream).instrument(span)) .await .change_context(TopLevelError::SocketHandlerFailure)?; } } /// Run a session. /// /// This is here because it's convenient, not because it shares state (obviously, /// given the type signature). But it's somewhat logically associated with this type, /// so it seems reasonable to make it an associated function. async fn run_session(handle: UnixStream) {} }