192 lines
6.1 KiB
Rust
192 lines
6.1 KiB
Rust
use dsn::DSN;
|
|
use miette::miette;
|
|
|
|
use colored::Colorize;
|
|
use comfy_table::{Attribute, Cell, Color, ContentArrangement, Table, presets::UTF8_FULL};
|
|
use miette::Result;
|
|
|
|
use crate::{
|
|
cli::ConnectionFormat,
|
|
config::{PGDConfig, Project},
|
|
consts::{DATABASE, USERNAME},
|
|
controller::{docker::DockerController, reconciler::Reconciler},
|
|
state::{InstanceState, StateManager},
|
|
};
|
|
|
|
mod docker;
|
|
mod utils;
|
|
|
|
pub mod reconciler;
|
|
|
|
pub struct Context {
|
|
docker: DockerController,
|
|
project: Option<Project>,
|
|
instance: Option<InstanceState>,
|
|
state: StateManager,
|
|
}
|
|
|
|
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()?;
|
|
|
|
let instance = match (project.as_ref(), instance_override) {
|
|
(None, None) => None,
|
|
// prioritizing provided instance name
|
|
(_, Some(instance)) => state.get(&instance),
|
|
(Some(project), None) => state.get(&project.name),
|
|
};
|
|
|
|
Ok(Self {
|
|
docker: DockerController::new().await?,
|
|
project,
|
|
instance,
|
|
state,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Main CLI command dispatcher
|
|
pub struct Controller {
|
|
ctx: Context,
|
|
}
|
|
impl Controller {
|
|
pub fn new(ctx: Context) -> Self {
|
|
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 };
|
|
|
|
if let Some(project) = &self.ctx.project {
|
|
return reconciler.reconcile(project).await;
|
|
}
|
|
|
|
println!("{}", "Initializing new pgd project...".cyan());
|
|
|
|
let mut versions = self.ctx.docker.available_versions().await?;
|
|
versions.sort();
|
|
let latest_version = versions
|
|
.last()
|
|
.ok_or(miette!("expected to have at least one version"))?;
|
|
|
|
let config = PGDConfig {
|
|
version: *latest_version,
|
|
password: utils::generate_password(),
|
|
port: utils::find_available_port()?,
|
|
};
|
|
let project = Project::new(config)?;
|
|
|
|
println!(
|
|
"\n{} {}\n",
|
|
"Created pgd.toml in",
|
|
project.path.display().to_string().bright_white().bold()
|
|
);
|
|
|
|
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),
|
|
]);
|
|
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("Port").fg(Color::White),
|
|
Cell::new(project.config.port.to_string()).add_attribute(Attribute::Bold),
|
|
]);
|
|
table.add_row(vec![
|
|
Cell::new("Password").fg(Color::White),
|
|
Cell::new("*".repeat(project.config.password.len())).fg(Color::DarkGrey),
|
|
]);
|
|
|
|
println!("{table}");
|
|
|
|
reconciler.reconcile(&project).await?;
|
|
|
|
println!("\n{}", "✓ Project initialized successfully!".green().bold());
|
|
|
|
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
|
|
}
|