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