1#![feature(exit_status_error)]
26
27use std::process::Command;
28use std::process::Stdio;
29
30use color_eyre::eyre::Context;
31use color_eyre::owo_colors::OwoColorize as _;
32use ytil_sys::cli::Args;
33
34use crate::pgpass::PgpassEntry;
35use crate::pgpass::PgpassFile;
36use crate::vault::VaultReadOutput;
37
38mod nvim_dbee;
39mod pgpass;
40mod vault;
41
42fn exec_vault_read_cmd(vault_path: &str) -> color_eyre::Result<VaultReadOutput> {
52 let mut cmd = Command::new("vault");
53 cmd.args(["read", vault_path, "--format=json"]);
54
55 let cmd_stdout = &cmd.output()?.stdout;
56
57 serde_json::from_slice(cmd_stdout).with_context(|| {
58 str::from_utf8(cmd_stdout).map_or_else(
59 |error| format!("cmd stdout invalid utf-8 | cmd={cmd:#?} error={error:?}"),
60 |str_stdout| format!("cannot build VaultReadOutput from vault cmd {cmd:#?} stdout {str_stdout:?}"),
61 )
62 })
63}
64
65fn main() -> color_eyre::Result<()> {
67 color_eyre::install()?;
68
69 let args = ytil_sys::cli::get();
70 if args.has_help() {
71 println!("{}", include_str!("../help.txt"));
72 return Ok(());
73 }
74
75 let pgpass_path = ytil_sys::dir::build_home_path(&[".pgpass"])?;
76 let pgpass_content = std::fs::read_to_string(&pgpass_path)?;
77 let pgpass_file = PgpassFile::parse(pgpass_content.as_str())?;
78
79 let args = ytil_sys::cli::get();
80 let Some(mut pgpass_entry) = ytil_tui::get_item_from_cli_args_or_select(
81 &args,
82 |(idx, _)| *idx == 0,
83 pgpass_file.entries,
84 |alias: &str| Box::new(move |entry: &PgpassEntry| entry.metadata.alias == alias),
85 )?
86 else {
87 return Ok(());
88 };
89
90 println!(
91 "\nLogging into Vault @ {}\n{}\n",
92 std::env::var("VAULT_ADDR")?.bold(),
93 "(be sure to have the VPN on!)".bold()
94 );
95 vault::log_into_vault_if_required()?;
96 let vault_read_output = exec_vault_read_cmd(&pgpass_entry.metadata.vault_path)?;
97
98 pgpass_entry.connection_params.update(&vault_read_output.data);
99 pgpass::save_new_pgpass_file(pgpass_file.idx_lines, &pgpass_entry.connection_params, &pgpass_path)?;
100
101 let nvim_dbee_conns_path = ytil_sys::dir::build_home_path(&[".local", "state", "nvim", "dbee", "conns.json"])?;
102 nvim_dbee::save_new_nvim_dbee_conns_file(&pgpass_entry, &nvim_dbee_conns_path)?;
103
104 println!(
105 "{} credentials updated in {}",
106 pgpass_entry.metadata.alias.green().bold(),
107 pgpass_path.display()
108 );
109 println!(
110 "{} credentials updated in {}",
111 pgpass_entry.metadata.alias.green().bold(),
112 nvim_dbee_conns_path.display()
113 );
114
115 println!(); if Some(true) == ytil_tui::yes_no_select(&format!("Connect to {}? ", pgpass_entry.metadata.alias))? {
118 let db_url = pgpass_entry.connection_params.db_url();
119 println!(
120 "\nConnecting to {} @\n\n{}\n",
121 pgpass_entry.metadata.alias.bold(),
122 db_url.bold()
123 );
124
125 if let Some(psql_exit_code) = Command::new("pgcli")
126 .arg(&db_url)
127 .stdin(Stdio::inherit())
128 .stdout(Stdio::inherit())
129 .stderr(Stdio::inherit())
130 .spawn()?
131 .wait()?
132 .code()
133 {
134 std::process::exit(psql_exit_code);
135 }
136
137 eprintln!("{}", format!("pgcli {db_url} terminated by signal.").red().bold());
138 std::process::exit(1);
139 }
140
141 Ok(())
142}