use std::process::ExitCode;
use clap::Parser;
use dsp_cli::cli::Cli;
use dsp_cli::config::Config;
use dsp_cli::diagnostic::ExitCategory;
use dsp_cli::render::MetaContext;
fn main() -> ExitCode {
let dsp_token_before_dotenv = std::env::var("DSP_TOKEN").ok();
let dsp_server_present_before_dotenv = std::env::var_os("DSP_SERVER").is_some();
dotenvy::dotenv().ok();
let cli = Cli::parse();
dsp_cli::diagnostic::init_tracing(cli.verbose);
if cross_context_token_risk(
dsp_token_before_dotenv.as_deref(),
dsp_server_present_before_dotenv,
std::env::var_os("DSP_SERVER").is_some(),
) {
tracing::warn!(
"DSP_TOKEN is set in your shell environment, but DSP_SERVER comes from a .env file in \
the current directory — you may be sending a token acquired for one server to a different one. \
See `dsp docs connecting`."
);
}
let notice_fmt = cli.output_format();
let server_flag = cli.server_flag().map(str::to_owned);
let allow_insecure = cli.allow_insecure_server;
let result = dsp_cli::run(cli);
let code = match result {
Ok(()) => ExitCategory::Success as u8,
Err(diag) => {
let code = diag.exit_category() as u8;
match notice_fmt {
Some(fmt) => {
let server_label = Config::resolve(server_flag.as_deref(), allow_insecure)
.ok()
.map(|c| c.server)
.unwrap_or_default();
let meta = MetaContext {
server_label,
auth_state: String::new(), filter_warning: None,
count_caveat: None,
count_cost: None,
};
let mut renderer = fmt.into_renderer();
let _ = renderer.diagnostic(&diag, &meta); }
None => eprintln!("Error: {diag}"), }
code
}
};
dsp_cli::update::maybe_notify(notice_fmt);
ExitCode::from(code)
}
fn cross_context_token_risk(
dsp_token_before_dotenv: Option<&str>,
dsp_server_present_before_dotenv: bool,
dsp_server_present_after_dotenv: bool,
) -> bool {
let token_from_process_env = dsp_token_before_dotenv.map(|t| !t.trim().is_empty()).unwrap_or(false);
let server_newly_from_dotenv = !dsp_server_present_before_dotenv && dsp_server_present_after_dotenv;
token_from_process_env && server_newly_from_dotenv
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn warns_when_token_from_process_env_and_server_newly_from_dotenv() {
assert!(cross_context_token_risk(Some("a-real-token"), false, true));
}
#[test]
fn no_warning_when_server_was_already_present_before_dotenv() {
assert!(!cross_context_token_risk(Some("a-real-token"), true, true));
}
#[test]
fn no_warning_when_dotenv_introduced_no_server() {
assert!(!cross_context_token_risk(Some("a-real-token"), false, false));
}
#[test]
fn no_warning_when_token_absent() {
assert!(!cross_context_token_risk(None, false, true));
}
#[test]
fn no_warning_when_token_blank() {
assert!(!cross_context_token_risk(Some(" "), false, true));
}
}