terrazzo-terminal 0.2.8

A simple web-based terminal emulator built on Terrazzo.
use scopeguard::defer;
use tracing::Instrument as _;
use tracing::debug;
use tracing::info;
use tracing::info_span;
use tracing::warn;

use crate::backend::Server;
use crate::backend::protos::terrazzo::terminal::ListTerminalsRequest;
use crate::backend::protos::terrazzo::terminal::MaybeString;
use crate::backend::protos::terrazzo::terminal::TerminalDef;
use crate::backend::protos::terrazzo::terminal::terminal_service_client::TerminalServiceClient;
use crate::processes;

pub async fn list_terminals(server: &Server, visited: Vec<String>) -> Vec<TerminalDef> {
    async {
        debug!("Start");
        defer!(debug!("Done"));
        let mut response = vec![];
        response.extend(processes::list::list().iter().map(|terminal| {
            let title = &terminal.title;
            TerminalDef {
                address: Some(terminal.address.clone().into()),
                shell_title: title.shell_title.clone(),
                override_title: title.override_title.clone().map(|s| MaybeString { s }),
                order: terminal.order,
                tile: terminal.tile.into(),
            }
        }));
        for client_name in server.connections().clients() {
            async {
                if visited.iter().any(|v| v.as_str() == client_name.as_ref()) {
                    info!("Already visited");
                    return;
                }
                let Some(client) = server.connections().get_client(&client_name) else {
                    warn!("Client connection not found");
                    return;
                };
                let mut client = TerminalServiceClient::new(client);
                let terminals = client.list_terminals(ListTerminalsRequest {
                    visited: visited.clone(),
                });
                let Ok(mut terminals) = terminals
                    .await
                    .inspect_err(|error| warn!("Failed: {error}"))
                    .map(|response| response.into_inner().terminals)
                else {
                    return;
                };
                for terminal in &mut terminals {
                    let client_address = terminal
                        .address
                        .get_or_insert_default()
                        .via
                        .get_or_insert_default();
                    client_address.via.push(client_name.to_string());
                }
                response.extend(terminals);
            }
            .instrument(info_span!("Client terminals", %client_name))
            .await
        }
        debug!("Result = {response:?}");
        return response;
    }
    .instrument(info_span!("List terminals"))
    .await
}