refactor: splitted controller and reconciler

This commit is contained in:
hdbg
2025-12-04 22:37:25 +01:00
parent bc37b58d80
commit c45e9305e5
10 changed files with 381 additions and 286 deletions

View File

@@ -1,80 +1,55 @@
use miette::{Context, IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};
use std::cell::{Ref, RefCell};
use std::collections::HashMap;
use std::path::PathBuf;
use crate::config::PostgresVersion;
/// State information for a single PostgreSQL instance
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstanceState {
/// Docker container ID
pub container_id: String,
/// PostgreSQL version running in the container
pub postgres_version: PostgresVersion,
/// Port the container is bound to
pub port: u16,
/// Timestamp when the instance was created (Unix timestamp)
pub created_at: u64,
}
/// Manages the global state file at ~/.pgd/state.json
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StateManager {
/// Map of project name to instance state
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
struct State {
#[serde(default)]
instances: HashMap<String, InstanceState>,
}
/// Get the path to the state file (~/.pgd/state.json)
fn state_file_path() -> Result<PathBuf> {
let home = std::env::var("HOME")
.into_diagnostic()
.wrap_err("Failed to get HOME environment variable")?;
Ok(PathBuf::from(home).join(".pgd").join("state.json"))
}
impl StateManager {
/// Load the state manager from disk, or create a new one if it doesn't exist
pub fn load() -> Result<Self> {
impl State {
fn new() -> Result<Self> {
let state_path = state_file_path()?;
if !state_path.exists() {
// Create the directory if it doesn't exist
if let Some(parent) = state_path.parent() {
std::fs::create_dir_all(parent)
.into_diagnostic()
.wrap_err("Failed to create .pgd directory")?;
}
// Return empty state
return Ok(StateManager {
instances: HashMap::new(),
});
return Ok(Self::default());
}
let content = std::fs::read_to_string(&state_path)
.into_diagnostic()
.wrap_err_with(|| format!("Failed to read state file: {}", state_path.display()))?;
let state: StateManager = serde_json::from_str(&content)
let state: Self = serde_json::from_str(&content)
.into_diagnostic()
.wrap_err("Failed to parse state.json")?;
Ok(state)
}
/// Save the state manager to disk
pub fn save(&self) -> Result<()> {
fn save(&self) -> Result<()> {
let state_path = state_file_path()?;
// Ensure directory exists
if let Some(parent) = state_path.parent() {
std::fs::create_dir_all(parent)
.into_diagnostic()
@@ -91,20 +66,30 @@ impl StateManager {
Ok(())
}
}
/// Get mutable state for a specific project
pub fn get_mut(&mut self, project_name: &str) -> Option<&mut InstanceState> {
self.instances.get_mut(project_name)
pub struct StateManager(RefCell<State>);
impl StateManager {
pub fn new() -> Result<Self> {
Ok(Self(RefCell::new(State::new()?)))
}
/// Set the state for a specific project
pub fn set(&mut self, project_name: String, state: InstanceState) {
self.instances.insert(project_name, state);
pub fn save(&self) -> Result<()> {
self.0.borrow().save()?;
Ok(())
}
/// Remove the state for a specific project
pub fn remove(&mut self, project_name: &str) -> Option<InstanceState> {
self.instances.remove(project_name)
pub fn get(&self, project_name: &str) -> Option<InstanceState> {
self.0.borrow().instances.get(project_name).cloned()
}
pub fn set(&self, project_name: String, state: InstanceState) {
self.0.borrow_mut().instances.insert(project_name, state);
}
pub fn remove(&self, project_name: &str) -> Option<InstanceState> {
self.0.borrow_mut().instances.remove(project_name)
}
}
@@ -122,4 +107,10 @@ impl InstanceState {
created_at: now,
}
}
}
}
fn state_file_path() -> Result<PathBuf> {
let home = std::env::home_dir().wrap_err("Failed to get HOME environment variable")?;
Ok(home.join(".pgd").join("state.json"))
}