sshattrick 0.1.1

Hockey in your terminal over SSH.
Documentation
use super::channel::AppChannel;
use crate::tui::Tui;
use crate::types::AppResult;
use anyhow::{anyhow, Context};
use russh::server::{self, Auth, Msg, Session};
use russh::{Channel, ChannelId, Pty};
use std::collections::HashMap;
use tokio::sync::mpsc::Sender;

pub struct AppClient {
    username: String,
    tui_sender: Sender<Tui>,
    channels: HashMap<ChannelId, AppChannel>,
}

impl AppClient {
    pub fn new(tui_sender: Sender<Tui>) -> Self {
        Self {
            username: String::new(),
            tui_sender,
            channels: HashMap::new(),
        }
    }

    fn channel_mut(&mut self, id: ChannelId) -> AppResult<&mut AppChannel> {
        self.channels
            .get_mut(&id)
            .with_context(|| format!("unknown channel: {id}"))
    }

    fn accept(&mut self, user: &str) -> AppResult<Auth> {
        self.username = user.to_string();
        Ok(Auth::Accept)
    }
}

impl server::Handler for AppClient {
    type Error = anyhow::Error;

    async fn auth_password(&mut self, user: &str, _password: &str) -> AppResult<Auth> {
        self.accept(user)
    }

    async fn auth_publickey(
        &mut self,
        user: &str,
        _public_key: &russh::keys::PublicKey,
    ) -> AppResult<Auth> {
        self.accept(user)
    }

    async fn channel_open_session(
        &mut self,
        channel: Channel<Msg>,
        _session: &mut Session,
    ) -> AppResult<bool> {
        let app_channel = AppChannel::new(self.username.clone());
        if self.channels.insert(channel.id(), app_channel).is_some() {
            return Err(anyhow!("channel `{}` has been already opened", channel.id()));
        }
        Ok(true)
    }

    async fn channel_close(&mut self, id: ChannelId, _: &mut Session) -> AppResult<()> {
        self.channels
            .remove(&id)
            .ok_or_else(|| anyhow!("channel `{id}` has been already closed"))?;
        Ok(())
    }

    async fn data(&mut self, id: ChannelId, data: &[u8], _: &mut Session) -> AppResult<()> {
        self.channel_mut(id)?.data(data).await
    }

    async fn pty_request(
        &mut self,
        id: ChannelId,
        _: &str,
        width: u32,
        height: u32,
        _: u32,
        _: u32,
        _: &[(Pty, u32)],
        session: &mut Session,
    ) -> AppResult<()> {
        let tui_sender = self.tui_sender.clone();
        self.channel_mut(id)?
            .pty_request(id, width, height, session.handle(), tui_sender)
            .await
    }

    async fn window_change_request(
        &mut self,
        id: ChannelId,
        width: u32,
        height: u32,
        _: u32,
        _: u32,
        _: &mut Session,
    ) -> AppResult<()> {
        self.channel_mut(id)?
            .window_change_request(width, height)
            .await
    }
}