1use std::process::Command;
9use std::process::Stdio;
10
11use owo_colors::OwoColorize;
12use rootcause::prelude::ResultExt;
13use ytil_sys::cli::Args;
14
15use crate::pgpass::PgpassEntry;
16use crate::pgpass::PgpassFile;
17use crate::vault::VaultReadOutput;
18
19mod nvim_dbee;
20mod pgpass;
21mod vault;
22
23#[ytil_sys::main]
25fn main() -> rootcause::Result<()> {
26 let args = ytil_sys::cli::get();
27 if args.has_help() {
28 println!("{}", include_str!("../help.txt"));
29 return Ok(());
30 }
31
32 let pgpass_path = ytil_sys::dir::build_home_path(&[".pgpass"])?;
33 let pgpass_content = std::fs::read_to_string(&pgpass_path)?;
34 let pgpass_file = PgpassFile::parse(pgpass_content.as_str())?;
35
36 let args = ytil_sys::cli::get();
37 let Some(mut pgpass_entry) = ytil_tui::get_item_from_cli_args_or_select(
38 &args,
39 |(idx, _)| *idx == 0,
40 pgpass_file.entries,
41 |alias: &str| Box::new(move |entry: &PgpassEntry| entry.metadata.alias == alias),
42 )?
43 else {
44 return Ok(());
45 };
46
47 println!(
48 "\nLogging into Vault @ {}\n{}\n",
49 std::env::var("VAULT_ADDR")?.bold(),
50 "(be sure to have the VPN on!)".bold()
51 );
52 vault::log_into_vault_if_required()?;
53 let vault_read_output = exec_vault_read_cmd(&pgpass_entry.metadata.vault_path)?;
54
55 pgpass_entry.connection_params.update(&vault_read_output.data);
56 pgpass::save_new_pgpass_file(pgpass_file.idx_lines, &pgpass_entry.connection_params, &pgpass_path)?;
57
58 let nvim_dbee_conns_path = ytil_sys::dir::build_home_path(&[".local", "state", "nvim", "dbee", "conns.json"])?;
59 nvim_dbee::save_new_nvim_dbee_conns_file(&pgpass_entry, &nvim_dbee_conns_path)?;
60
61 println!(
62 "{} credentials updated in {}",
63 pgpass_entry.metadata.alias.green().bold(),
64 pgpass_path.display()
65 );
66 println!(
67 "{} credentials updated in {}",
68 pgpass_entry.metadata.alias.green().bold(),
69 nvim_dbee_conns_path.display()
70 );
71
72 println!(); if Some(true) == ytil_tui::yes_no_select(&format!("Connect to {}? ", pgpass_entry.metadata.alias))? {
75 let db_url = pgpass_entry.connection_params.db_url();
76 println!(
77 "\nConnecting to {} @\n\n{}\n",
78 pgpass_entry.metadata.alias.bold(),
79 db_url.bold()
80 );
81
82 if let Some(psql_exit_code) = Command::new("pgcli")
83 .arg(&db_url)
84 .stdin(Stdio::inherit())
85 .stdout(Stdio::inherit())
86 .stderr(Stdio::inherit())
87 .spawn()?
88 .wait()?
89 .code()
90 {
91 std::process::exit(psql_exit_code);
92 }
93
94 eprintln!("{}", format!("pgcli {db_url} terminated by signal.").red().bold());
95 std::process::exit(1);
96 }
97
98 Ok(())
99}
100
101fn exec_vault_read_cmd(vault_path: &str) -> rootcause::Result<VaultReadOutput> {
106 let mut cmd = Command::new("vault");
107 cmd.args(["read", vault_path, "--format=json"]);
108
109 let cmd_stdout = &cmd.output()?.stdout;
110
111 Ok(serde_json::from_slice(cmd_stdout)
112 .context("error deserializing vault command output")
113 .attach_with(|| {
114 str::from_utf8(cmd_stdout).map_or_else(
115 |error| format!("cmd={cmd:#?} error={error:?}"),
116 |str_stdout| format!("cmd={cmd:#?} stdout={str_stdout:?}"),
117 )
118 })?)
119}