athena_rs 3.23.0

Hyper performant polyglot Database driver
Documentation
use clap::error::ErrorKind;
use std::path::Path;

const TRACING_LOG_DIR_ENV: &str = "ATHENA_TRACING_LOG_DIR";

pub fn cli_parse_hint(args: &[String], error_kind: ErrorKind) -> Option<String> {
    if !matches!(
        error_kind,
        ErrorKind::InvalidSubcommand | ErrorKind::UnknownArgument
    ) {
        return None;
    }

    if !looks_like_cargo_build_invocation(args) {
        return None;
    }

    Some(
        "`athena_rs` is the runtime CLI, not Cargo. Build the binary with `cargo build --release`. After building, run Athena with `target/release/athena_rs server` on Linux/macOS or `target\\release\\athena_rs.exe server` on Windows. For a dev run, use `cargo run --bin athena_rs -- server`."
            .to_string(),
    )
}

pub fn tracing_log_permission_hint(
    permission_denied_path: &Path,
    selected_log_dir: Option<&Path>,
) -> Option<String> {
    #[cfg(unix)]
    {
        let target: String = sh_single_quote(&permission_denied_path.display().to_string());
        let export_target: String = sh_single_quote(
            &selected_log_dir
                .unwrap_or(permission_denied_path)
                .display()
                .to_string(),
        );
        return Some(format!(
            "To keep Athena logging there, run `sudo mkdir -p '{target}' && sudo chown \"$USER\":\"$USER\" '{target}' && sudo chmod 755 '{target}'`. For systemd, replace `$USER` with the service user/group (for example `athena`). Or pin a writable directory with `export {TRACING_LOG_DIR_ENV}='{export_target}'`."
        ));
    }

    #[cfg(windows)]
    {
        let target: String = ps_single_quote(&permission_denied_path.display().to_string());
        let export_target: String = ps_single_quote(
            &selected_log_dir
                .unwrap_or(permission_denied_path)
                .display()
                .to_string(),
        );
        return Some(format!(
            "PowerShell fix: `New-Item -ItemType Directory -Force -Path '{target}' | Out-Null; icacls '{target}' /grant \"$env:USERNAME:(OI)(CI)M\"`. Or pin a writable directory with `$env:{TRACING_LOG_DIR_ENV} = '{export_target}'`."
        ));
    }

    #[cfg(not(any(unix, windows)))]
    {
        let _ = permission_denied_path;
        let _ = selected_log_dir;
        None
    }
}

fn looks_like_cargo_build_invocation(args: &[String]) -> bool {
    args.iter()
        .skip(1)
        .any(|arg| matches!(arg.as_str(), "build" | "--release" | "--bin" | "--features"))
}

#[cfg(unix)]
fn sh_single_quote(value: &str) -> String {
    value.replace('\'', "'\"'\"'")
}

#[cfg(windows)]
fn ps_single_quote(value: &str) -> String {
    value.replace('\'', "''")
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::Path;

    #[test]
    fn cargo_build_invocation_receives_runtime_cli_hint() {
        let args: Vec<String> = vec![
            "athena_rs".to_string(),
            "build".to_string(),
            "--release".to_string(),
        ];

        let hint: String = cli_parse_hint(&args, ErrorKind::InvalidSubcommand)
            .expect("expected Athena-specific cargo build hint");

        assert!(hint.contains("cargo build --release"));
        assert!(hint.contains("cargo run --bin athena_rs -- server"));
    }

    #[test]
    fn normal_cli_args_do_not_receive_build_hint() {
        let args = vec!["athena_rs".to_string(), "server".to_string()];
        assert!(cli_parse_hint(&args, ErrorKind::InvalidSubcommand).is_none());
    }

    #[test]
    fn tracing_permission_hint_mentions_env_override() {
        let hint = tracing_log_permission_hint(
            Path::new("/var/log/athena"),
            Some(Path::new("./var/log/athena")),
        )
        .expect("expected permission hint");

        assert!(hint.contains(TRACING_LOG_DIR_ENV));
        #[cfg(unix)]
        assert!(hint.contains("sudo mkdir -p"));
        #[cfg(windows)]
        assert!(hint.contains("icacls"));
    }
}