zero-trust-rps 0.0.4

Online Multiplayer Rock Paper Scissors
Documentation
use std::{fmt::Display, future::Future, process::ExitCode};
use stderrlog::{LogLevelNum, Timestamp};

use crate::common::{constants::CRATE_VERSION, message::CommitHash, result::DynResult};

shadow_rs::shadow!(build);

#[expect(clippy::const_is_empty)]
pub(super) const COMMIT_HASH: Option<CommitHash> = if build::COMMIT_HASH.is_empty() {
    None
} else {
    Some(CommitHash::from_hex_str(build::COMMIT_HASH))
};

const TAG_BRANCH: &str = {
    #[expect(clippy::const_is_empty)]
    if build::TAG.is_empty() {
        #[expect(clippy::const_is_empty)]
        if build::BRANCH.is_empty() {
            ""
        } else {
            shadow_rs::concatcp!("\nbranch:       ", build::BRANCH)
        }
    } else {
        shadow_rs::concatcp!("\ntag:          ", build::TAG)
    }
};
const COMMIT: &str = {
    const _COMMIT: &str = if COMMIT_HASH.is_some() {
        COMMIT_HASH.as_ref().unwrap().as_str()
    } else {
        ""
    };
    if COMMIT_HASH.is_some() {
        shadow_rs::concatcp!(
            "\ncommit:       ",
            _COMMIT,
            "\ncommit time:  ",
            build::COMMIT_DATE,
        )
    } else {
        ""
    }
};

pub(super) const CLAP_LONG_VERSION: &str = shadow_rs::concatcp!(
    CRATE_VERSION,
    TAG_BRANCH,
    COMMIT,
    "\nbuild target: ",
    build::BUILD_TARGET,
    // TODO: "\ntarget cpu: ",
    // TODO: build::TARGET_CPU,
    "\nrust version: ",
    build::RUST_VERSION,
    "\nbuild os:     ",
    build::BUILD_OS,
);

#[allow(dead_code)]
#[inline]
pub(crate) fn run(fut: impl Future<Output = DynResult<()>>) -> DynResult<()> {
    run_with_runtime(&create_runtime()?, fut)
}

#[inline]
fn create_runtime() -> std::io::Result<tokio::runtime::Runtime> {
    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
}

#[inline]
fn run_with_runtime<Err>(
    runtime: &tokio::runtime::Runtime,
    fut: impl Future<Output = Result<(), Err>>,
) -> Result<(), Err> {
    runtime.block_on(fut)
}

#[inline]
pub(crate) fn print_error<const LOG_TO_STDOUT: bool, Err: Display>(
    result: Result<(), Err>,
) -> std::process::ExitCode {
    match result {
        Ok(()) => ExitCode::SUCCESS,
        Err(err) => {
            if LOG_TO_STDOUT {
                println!("Error: {err}");
            } else {
                log::error!("Exiting with error: {err}");
            }
            ExitCode::FAILURE
        }
    }
}

pub fn configure_logging(module: &'static str) -> Result<(), log::SetLoggerError> {
    stderrlog::new()
        .module(module)
        .quiet(false)
        .verbosity(if cfg!(debug_assertions) {
            LogLevelNum::Trace
        } else {
            LogLevelNum::Debug
        })
        .timestamp(Timestamp::Second) // TODO: timestamp format without redundant timezone??
        .show_module_names(false)
        .color(stderrlog::ColorChoice::Auto)
        .show_level(true)
        .init()
}