summaryrefslogtreecommitdiffstats
path: root/src/db
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2020-11-10 08:31:24 +0100
committerMatthias Beyer <mail@beyermatthias.de>2020-11-10 08:31:33 +0100
commit596e863e92bd606458a76c3d2a5fb0da83b20dda (patch)
treef95b40c2e058443d27ac0777500e214451248eda /src/db
parent451b417f547c9c58a66847bf472e82664c87460b (diff)
Move DB cli interface to proper place
This moves the DB CLI interface building code to src/cli.rs and the interface handling code to src/commands/db.rs Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
Diffstat (limited to 'src/db')
-rw-r--r--src/db/cli.rs68
-rw-r--r--src/db/interface.rs245
-rw-r--r--src/db/mod.rs6
3 files changed, 0 insertions, 319 deletions
diff --git a/src/db/cli.rs b/src/db/cli.rs
deleted file mode 100644
index d883bf7..0000000
--- a/src/db/cli.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-use clap_v3 as clap;
-use clap::App;
-use clap::Arg;
-use clap::crate_authors;
-use clap::crate_version;
-
-pub fn cli<'a>() -> App<'a> {
- App::new("db")
- .author(crate_authors!())
- .version(crate_version!())
- .about("Database CLI interface")
-
- .subcommand(App::new("cli")
- .about("Start a database CLI, if installed on the current host")
- .long_about(indoc::indoc!(r#"
- Starts a database shell on the configured database using one of the following
- programs:
- - psql
- - pgcli
-
- if installed.
- "#))
-
- .arg(Arg::with_name("tool")
- .required(false)
- .multiple(false)
- .long("tool")
- .value_name("TOOL")
- .possible_values(&["psql", "pgcli"])
- .help("Use a specific tool")
- )
- )
-
- .subcommand(App::new("artifacts")
- .about("List artifacts from the DB")
- .arg(Arg::with_name("csv")
- .required(false)
- .multiple(false)
- .long("csv")
- .takes_value(false)
- .help("Format output as CSV")
- )
- )
-
- .subcommand(App::new("envvars")
- .about("List envvars from the DB")
- .arg(Arg::with_name("csv")
- .required(false)
- .multiple(false)
- .long("csv")
- .takes_value(false)
- .help("Format output as CSV")
- )
- )
-
- .subcommand(App::new("images")
- .about("List images from the DB")
- .arg(Arg::with_name("csv")
- .required(false)
- .multiple(false)
- .long("csv")
- .takes_value(false)
- .help("Format output as CSV")
- )
- )
-
-}
-
diff --git a/src/db/interface.rs b/src/db/interface.rs
deleted file mode 100644
index 1f0f544..0000000
--- a/src/db/interface.rs
+++ /dev/null
@@ -1,245 +0,0 @@
-use std::fmt::Display;
-use std::path::PathBuf;
-use std::process::Command;
-
-use clap_v3 as clap;
-use anyhow::Context;
-use anyhow::Error;
-use anyhow::Result;
-use anyhow::anyhow;
-use clap::ArgMatches;
-use diesel::RunQueryDsl;
-use itertools::Itertools;
-
-use crate::db::DbConnectionConfig;
-use crate::db::models;
-
-pub fn interface(db_connection_config: DbConnectionConfig, matches: &ArgMatches) -> Result<()> {
- match matches.subcommand() {
- ("cli", Some(matches)) => cli(db_connection_config, matches),
- ("artifacts", Some(matches)) => artifacts(db_connection_config, matches),
- ("envvars", Some(matches)) => envvars(db_connection_config, matches),
- ("images", Some(matches)) => images(db_connection_config, matches),
- (other, _) => return Err(anyhow!("Unknown subcommand: {}", other)),
- }
-}
-
-fn cli(db_connection_config: DbConnectionConfig, matches: &ArgMatches) -> Result<()> {
- trait PgCliCommand {
- fn run_for_uri(&self, dbcc: DbConnectionConfig) -> Result<()>;
- }
-
- struct Psql(PathBuf);
- impl PgCliCommand for Psql {
- fn run_for_uri(&self, dbcc: DbConnectionConfig) -> Result<()> {
- Command::new(&self.0)
- .arg(format!("--dbname={}", dbcc.database_name()))
- .arg(format!("--host={}", dbcc.database_host()))
- .arg(format!("--port={}", dbcc.database_port()))
- .arg(format!("--username={}", dbcc.database_user()))
- .stdin(std::process::Stdio::inherit())
- .stdout(std::process::Stdio::inherit())
- .stderr(std::process::Stdio::inherit())
- .output()
- .map_err(Error::from)
- .and_then(|out| {
- if out.status.success() {
- info!("pgcli exited successfully");
- Ok(())
- } else {
- Err(anyhow!("gpcli did not exit successfully"))
- .with_context(|| {
- match String::from_utf8(out.stderr) {
- Ok(log) => anyhow!("{}", log),
- Err(e) => anyhow!("Cannot parse log into valid UTF-8: {}", e),
- }
- })
- .map_err(Error::from)
- }
- })
- }
- }
-
- struct PgCli(PathBuf);
- impl PgCliCommand for PgCli {
- fn run_for_uri(&self, dbcc: DbConnectionConfig) -> Result<()> {
- Command::new(&self.0)
- .arg("--host")
- .arg(dbcc.database_host())
- .arg("--port")
- .arg(dbcc.database_port())
- .arg("--username")
- .arg(dbcc.database_user())
- .arg(dbcc.database_name())
- .stdin(std::process::Stdio::inherit())
- .stdout(std::process::Stdio::inherit())
- .stderr(std::process::Stdio::inherit())
- .output()
- .map_err(Error::from)
- .and_then(|out| {
- if out.status.success() {
- info!("pgcli exited successfully");
- Ok(())
- } else {
- Err(anyhow!("gpcli did not exit successfully"))
- .with_context(|| {
- match String::from_utf8(out.stderr) {
- Ok(log) => anyhow!("{}", log),
- Err(e) => anyhow!("Cannot parse log into valid UTF-8: {}", e),
- }
- })
- .map_err(Error::from)
- }
- })
-
- }
- }
-
-
- matches.value_of("tool")
- .map(|s| vec![s])
- .unwrap_or_else(|| vec!["psql", "pgcli"])
- .into_iter()
- .filter_map(|s| which::which(&s).ok().map(|path| (path, s)))
- .map(|(path, s)| {
- match s {
- "psql" => Ok(Box::new(Psql(path)) as Box<dyn PgCliCommand>),
- "pgcli" => Ok(Box::new(PgCli(path)) as Box<dyn PgCliCommand>),
- prog => Err(anyhow!("Unsupported pg CLI program: {}", prog)),
- }
- })
- .next()
- .transpose()?
- .ok_or_else(|| anyhow!("No Program found"))?
- .run_for_uri(db_connection_config)
-}
-
-fn artifacts(conn_cfg: DbConnectionConfig, matches: &ArgMatches) -> Result<()> {
- use crate::schema::artifacts::dsl;
-
- let csv = matches.is_present("csv");
- let hdrs = mk_header(vec!["id", "path"]);
- let conn = crate::db::establish_connection(conn_cfg)?;
- let data = dsl::artifacts
- .load::<models::Artifact>(&conn)?
- .into_iter()
- .map(|artifact| vec![format!("{}", artifact.id), artifact.path])
- .collect::<Vec<_>>();
-
- if data.is_empty() {
- info!("No artifacts in database");
- } else {
- display_data(hdrs, data, csv)?;
- }
-
- Ok(())
-}
-
-fn envvars(conn_cfg: DbConnectionConfig, matches: &ArgMatches) -> Result<()> {
- use crate::schema::envvars::dsl;
-
- let csv = matches.is_present("csv");
- let hdrs = mk_header(vec!["id", "name", "value"]);
- let conn = crate::db::establish_connection(conn_cfg)?;
- let data = dsl::envvars
- .load::<models::EnvVar>(&conn)?
- .into_iter()
- .map(|evar| {
- vec![format!("{}", evar.id), evar.name, evar.value]
- })
- .collect::<Vec<_>>();
-
- if data.is_empty() {
- info!("No environment variables in database");
- } else {
- display_data(hdrs, data, csv)?;
- }
-
- Ok(())
-}
-
-fn images(conn_cfg: DbConnectionConfig, matches: &ArgMatches) -> Result<()> {
- use crate::schema::images::dsl;
-
- let csv = matches.is_present("csv");
- let hdrs = mk_header(vec!["id", "name"]);
- let conn = crate::db::establish_connection(conn_cfg)?;
- let data = dsl::images
- .load::<models::Image>(&conn)?
- .into_iter()
- .map(|image| {
- vec![format!("{}", image.id), image.name]
- })
- .collect::<Vec<_>>();
-
- if data.is_empty() {
- info!("No images in database");
- } else {
- display_data(hdrs, data, csv)?;
- }
-
- Ok(())
-}
-
-fn mk_header(vec: Vec<&str>) -> Vec<ascii_table::Column> {
- vec.into_iter()
- .map(|name| {
- let mut column = ascii_table::Column::default();
- column.header = name.into();
- column.align = ascii_table::Align::Left;
- column
- })
- .collect()
-}
-
-/// Display the passed data as nice ascii table,
-/// or, if stdout is a pipe, print it nicely parseable
-fn display_data<D: Display>(headers: Vec<ascii_table::Column>, data: Vec<Vec<D>>, csv: bool) -> Result<()> {
- use std::io::Write;
-
- if csv {
- use csv::WriterBuilder;
- let mut wtr = WriterBuilder::new().from_writer(vec![]);
- for record in data.into_iter() {
- let r: Vec<String> = record.into_iter()
- .map(|e| e.to_string())
- .collect();
-
- wtr.write_record(&r)?;
- }
-
- let out = std::io::stdout();
- let mut lock = out.lock();
-
- wtr.into_inner()
- .map_err(Error::from)
- .and_then(|t| String::from_utf8(t).map_err(Error::from))
- .and_then(|text| writeln!(lock, "{}", text).map_err(Error::from))
-
- } else {
- if atty::is(atty::Stream::Stdout) {
- let mut ascii_table = ascii_table::AsciiTable::default();
-
- ascii_table.max_width = terminal_size::terminal_size()
- .map(|tpl| tpl.0.0 as usize) // an ugly interface indeed!
- .unwrap_or(80);
-
- headers.into_iter()
- .enumerate()
- .for_each(|(i, c)| {
- ascii_table.columns.insert(i, c);
- });
-
- ascii_table.print(data);
- Ok(())
- } else {
- let out = std::io::stdout();
- let mut lock = out.lock();
- for list in data {
- writeln!(lock, "{}", list.iter().map(|d| d.to_string()).join(" "))?;
- }
- Ok(())
- }
- }
-}
-
diff --git a/src/db/mod.rs b/src/db/mod.rs
index f16b879..8134951 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -1,11 +1,5 @@
mod connection;
pub use connection::*;
-mod cli;
-pub use cli::*;
-
-mod interface;
-pub use interface::*;
-
pub mod models;