feat(cli): add connection command with DSN and human formats
This commit is contained in:
67
Cargo.lock
generated
67
Cargo.lock
generated
@@ -249,6 +249,20 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||
|
||||
[[package]]
|
||||
name = "cliclack"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2381872509dfa50d8b92b92a5da8367ba68458ab9494be4134b57ad6ca26295f"
|
||||
dependencies = [
|
||||
"console 0.15.11",
|
||||
"indicatif",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
@@ -275,6 +289,19 @@ dependencies = [
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width 0.2.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.16.1"
|
||||
@@ -382,6 +409,15 @@ dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dsn"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a68ec86c8ab056c40c4d3f6a543ad0ad6a251d7d01dac251feed242aa44e754a"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.20"
|
||||
@@ -841,7 +877,7 @@ version = "0.18.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88"
|
||||
dependencies = [
|
||||
"console",
|
||||
"console 0.16.1",
|
||||
"portable-atomic",
|
||||
"unicode-segmentation",
|
||||
"unicode-width 0.2.2",
|
||||
@@ -1064,8 +1100,10 @@ version = "0.0.1"
|
||||
dependencies = [
|
||||
"bollard",
|
||||
"clap",
|
||||
"cliclack",
|
||||
"colored",
|
||||
"comfy-table",
|
||||
"dsn",
|
||||
"futures",
|
||||
"indicatif",
|
||||
"miette",
|
||||
@@ -1408,6 +1446,12 @@ version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "smawk"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.1"
|
||||
@@ -1489,6 +1533,7 @@ version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
|
||||
dependencies = [
|
||||
"smawk",
|
||||
"unicode-linebreak",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
@@ -2176,6 +2221,26 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.3"
|
||||
|
||||
@@ -9,8 +9,10 @@ license = "MIT"
|
||||
[dependencies]
|
||||
bollard = "0.19.4"
|
||||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
cliclack = "0.3.7"
|
||||
colored = "3.0.0"
|
||||
comfy-table = "7.2.1"
|
||||
dsn = "1.2.1"
|
||||
futures = "0.3.31"
|
||||
indicatif = { version = "0.18.3", features = ["improved_unicode"] }
|
||||
miette = { version = "7.6.0", features = ["fancy"] }
|
||||
|
||||
10
src/cli.rs
10
src/cli.rs
@@ -36,13 +36,19 @@ pub enum ControlCommands {
|
||||
/// Restart postgres instance
|
||||
Restart,
|
||||
/// (WARNING!) Destroy postgres instance
|
||||
Destroy,
|
||||
Destroy { accept: bool },
|
||||
/// (WARNING!) Destruct database
|
||||
Wipe { accept: bool },
|
||||
|
||||
/// Status of instance
|
||||
Status,
|
||||
/// View logs produced by postgres
|
||||
Logs { follow: bool },
|
||||
/// (Sensitive) get connection details
|
||||
Connection { format: ConnectionFormat },
|
||||
Connection {
|
||||
#[arg(short, long, default_value = "dsn")]
|
||||
format: ConnectionFormat,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use miette::{Diagnostic, bail, miette};
|
||||
use dsn::DSN;
|
||||
use miette::miette;
|
||||
|
||||
use colored::Colorize;
|
||||
use comfy_table::{Attribute, Cell, Color, ContentArrangement, Table, presets::UTF8_FULL};
|
||||
use miette::Result;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
config::{PGDConfig, PostgresVersion, Project},
|
||||
cli::ConnectionFormat,
|
||||
config::{PGDConfig, Project},
|
||||
consts::{DATABASE, USERNAME},
|
||||
controller::{docker::DockerController, reconciler::Reconciler},
|
||||
state::{InstanceState, StateManager},
|
||||
};
|
||||
@@ -26,6 +26,16 @@ pub struct Context {
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn require_instance(&self) -> Result<&InstanceState> {
|
||||
self.instance.as_ref().ok_or(miette!("This command requires instance. Either initiliaze a project, or pass -I with instance name"))
|
||||
}
|
||||
|
||||
pub fn require_project(&self) -> Result<&Project> {
|
||||
self.project.as_ref().ok_or(miette!(
|
||||
"This command requires project. Please, initiliaze a project."
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn new(instance_override: Option<String>) -> Result<Self> {
|
||||
let project = Project::load()?;
|
||||
let state = StateManager::new()?;
|
||||
@@ -55,6 +65,59 @@ impl Controller {
|
||||
Self { ctx }
|
||||
}
|
||||
|
||||
pub async fn show_connection(&self, format: ConnectionFormat) -> Result<()> {
|
||||
let project = self.ctx.require_project()?;
|
||||
let reconciler = Reconciler { ctx: &self.ctx };
|
||||
|
||||
reconciler.reconcile(project).await?;
|
||||
|
||||
match format {
|
||||
ConnectionFormat::DSN => {
|
||||
let dsn = DSN::builder()
|
||||
.driver("postgres")
|
||||
.username(USERNAME)
|
||||
.password(project.config.password.clone())
|
||||
.host("127.0.0.1")
|
||||
.port(project.config.port)
|
||||
.database(DATABASE)
|
||||
.build();
|
||||
println!("{}", dsn.to_string());
|
||||
}
|
||||
ConnectionFormat::Human => {
|
||||
let mut table = create_ui_table("Instance");
|
||||
table.add_row(vec![
|
||||
Cell::new("Project").fg(Color::White),
|
||||
Cell::new(&project.name).add_attribute(Attribute::Bold),
|
||||
]);
|
||||
table.add_row(vec![
|
||||
Cell::new("PostgreSQL Version").fg(Color::White),
|
||||
Cell::new(project.config.version.to_string()).add_attribute(Attribute::Bold),
|
||||
]);
|
||||
table.add_row(vec![
|
||||
Cell::new("Host").fg(Color::White),
|
||||
Cell::new("127.0.0.1").add_attribute(Attribute::Bold),
|
||||
]);
|
||||
|
||||
table.add_row(vec![
|
||||
Cell::new("Port").fg(Color::White),
|
||||
Cell::new(project.config.port.to_string()).add_attribute(Attribute::Bold),
|
||||
]);
|
||||
table.add_row(vec![
|
||||
Cell::new("Username").fg(Color::White),
|
||||
Cell::new(USERNAME).add_attribute(Attribute::Bold),
|
||||
]);
|
||||
|
||||
table.add_row(vec![
|
||||
Cell::new("Password").fg(Color::White),
|
||||
Cell::new(project.config.password.clone()).fg(Color::DarkGrey),
|
||||
]);
|
||||
println!("{}", table);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn init_project(&self) -> Result<()> {
|
||||
let reconciler = Reconciler { ctx: &self.ctx };
|
||||
|
||||
@@ -83,20 +146,7 @@ impl Controller {
|
||||
project.path.display().to_string().bright_white().bold()
|
||||
);
|
||||
|
||||
let mut table = Table::new();
|
||||
table
|
||||
.load_preset(UTF8_FULL)
|
||||
.set_content_arrangement(ContentArrangement::Dynamic)
|
||||
.set_style(comfy_table::TableComponent::MiddleIntersections, ' ')
|
||||
.set_header(vec![
|
||||
Cell::new("Instance Configuration").add_attribute(Attribute::Bold),
|
||||
]);
|
||||
|
||||
use comfy_table::TableComponent::*;
|
||||
table.set_style(TopLeftCorner, '╭');
|
||||
table.set_style(TopRightCorner, '╮');
|
||||
table.set_style(BottomLeftCorner, '╰');
|
||||
table.set_style(BottomRightCorner, '╯');
|
||||
let mut table = create_ui_table("Project Configuration");
|
||||
table.add_row(vec![
|
||||
Cell::new("Project").fg(Color::White),
|
||||
Cell::new(&project.name).add_attribute(Attribute::Bold),
|
||||
@@ -123,3 +173,19 @@ impl Controller {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn create_ui_table(header: &'static str) -> Table {
|
||||
let mut table = Table::new();
|
||||
table
|
||||
.load_preset(UTF8_FULL)
|
||||
.set_content_arrangement(ContentArrangement::Dynamic)
|
||||
.set_style(comfy_table::TableComponent::MiddleIntersections, ' ')
|
||||
.set_header(vec![Cell::new(header).add_attribute(Attribute::Bold)]);
|
||||
|
||||
use comfy_table::TableComponent::*;
|
||||
table.set_style(TopLeftCorner, '╭');
|
||||
table.set_style(TopRightCorner, '╮');
|
||||
table.set_style(BottomLeftCorner, '╰');
|
||||
table.set_style(BottomRightCorner, '╯');
|
||||
table
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use miette::{Diagnostic, bail, miette};
|
||||
use miette::{Diagnostic, bail};
|
||||
|
||||
use colored::Colorize;
|
||||
use comfy_table::{Attribute, Cell, Color, ContentArrangement, Table, presets::UTF8_FULL};
|
||||
use miette::Result;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
config::{PGDConfig, PostgresVersion, Project},
|
||||
config::{PostgresVersion, Project},
|
||||
controller::{
|
||||
Context,
|
||||
docker::{self, DockerController},
|
||||
docker::{self},
|
||||
},
|
||||
state::{InstanceState, StateManager},
|
||||
state::InstanceState,
|
||||
};
|
||||
|
||||
const MAX_RETRIES: usize = 10;
|
||||
|
||||
@@ -34,10 +34,15 @@ async fn main() -> Result<()> {
|
||||
ControlCommands::Start => {}
|
||||
ControlCommands::Stop => {}
|
||||
ControlCommands::Restart => {}
|
||||
ControlCommands::Destroy => {}
|
||||
ControlCommands::Destroy { accept } => {}
|
||||
ControlCommands::Logs { follow } => todo!(),
|
||||
ControlCommands::Status => {}
|
||||
ControlCommands::Connection { format: _ } => {}
|
||||
// can't override an instance for this command, because password is in config
|
||||
ControlCommands::Connection { format } => {
|
||||
let ctx = Context::new(None).await?;
|
||||
Controller::new(ctx).show_connection(format).await?;
|
||||
}
|
||||
ControlCommands::Wipe { accept } => {}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use miette::{Context, IntoDiagnostic, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user