🏡 index : ~doyle/pisshoff.git

use async_trait::async_trait;
use itertools::Itertools;
use thrussh::ChannelId;

use crate::{
    command::{Command, CommandResult},
    server::{ConnectionState, ThrusshSession},
};

#[derive(Debug, Clone)]
pub struct Echo {}

#[async_trait]
impl Command for Echo {
    async fn new<S: ThrusshSession + Send>(
        _connection: &mut ConnectionState,
        params: &[String],
        channel: ChannelId,
        session: &mut S,
    ) -> CommandResult<Self> {
        let suffix = if session.redirected() { "" } else { "\n" };

        session.data(
            channel,
            format!("{}{suffix}", params.iter().join(" ")).into(),
        );

        CommandResult::Exit(0)
    }

    async fn stdin<S: ThrusshSession + Send>(
        self,
        _connection: &mut ConnectionState,
        _channel: ChannelId,
        _data: &[u8],
        _session: &mut S,
    ) -> CommandResult<Self> {
        CommandResult::Exit(0)
    }
}

#[cfg(test)]
mod test {
    use mockall::predicate::always;
    use test_case::test_case;

    use crate::{
        command::{echo::Echo, Command, CommandResult},
        server::{
            test::{fake_channel_id, predicate::eq_string},
            ConnectionState, MockThrusshSession,
        },
    };

    #[test_case(&[], "\n"; "no parameters")]
    #[test_case(&["hello"], "hello\n"; "single parameter")]
    #[test_case(&["hello", "world"], "hello world\n"; "multiple parameters")]
    #[tokio::test]
    async fn test(params: &[&str], output: &'static str) {
        let mut session = MockThrusshSession::default();

        session
            .expect_data()
            .once()
            .with(always(), eq_string(output))
            .returning(|_, _| ());

        session.expect_redirected().returning(|| false);

        let out = Echo::new(
            &mut ConnectionState::mock(),
            params
                .iter()
                .map(ToString::to_string)
                .collect::<Vec<_>>()
                .as_slice(),
            fake_channel_id(),
            &mut session,
        )
        .await;

        assert!(matches!(out, CommandResult::Exit(0)), "{out:?}");
    }
}