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