signstar-client 0.1.0

Library and executable for creating, reading and writing of signing requests for files
Documentation
//! Application for the creation of signing requests and their optional sending via SSH.

use std::process::ExitCode;

use clap::Parser;
use signstar_client::{
    Error,
    cli::{Cli, Command, SendCommand},
    ssh::client::ConnectConfig,
};
use signstar_common::{
    logging::setup_terminal_logging,
    request_api::{Request, Response},
};

/// Sends a signing request over SSH.
///
/// # Errors
///
/// Returns an error if
/// - [`ConnectOptions`] can not be created from the `send_command`,
/// - an SSH session can not be created when connecting using the [`ConnectOptions`],
/// - sending a signing request for a file fails,
/// - receiving a signature for a signing request fails,
/// - closing the SSH connection fails.
async fn send_request_via_ssh(send_command: SendCommand) -> Result<Response, Error> {
    let options = if let Some(config) = send_command.config {
        ConnectConfig::from_config_file(config)?
    } else {
        ConnectConfig::from_first_system_config()?
    };
    let mut session = options.connect(&send_command.user).await?;
    let response: Response = session
        .send(&Request::for_file(send_command.input)?)
        .await?;
    session.close().await?;
    Ok(response)
}

async fn run_command(args: Cli) -> Result<(), Error> {
    match args.command {
        Command::Prepare(prepare) => {
            Request::for_file(prepare.input)?.to_writer(std::io::stdout())?;
        }
        Command::Send(send_command) => {
            send_request_via_ssh(send_command)
                .await?
                .to_writer(std::io::stdout())?;
        }
    }
    Ok(())
}

/// Prepares and optionally sends a signing request.
///
/// # Errors
///
/// Returns an error if
/// - setting logging fails,
/// - preparing signing request fails,
/// - sending signing request fails.
#[tokio::main]
async fn main() -> ExitCode {
    let args = Cli::parse();

    if let Err(error) = setup_terminal_logging(args.verbosity) {
        eprintln!("{error}");
        return ExitCode::FAILURE;
    }

    let result = run_command(args).await;

    if let Err(error) = result {
        log::error!(error:err; "Processing signing request failed: {error:#?}");
        ExitCode::FAILURE
    } else {
        ExitCode::SUCCESS
    }
}