#![allow(unused_variables)]
pub mod config;
pub mod link_manager;
pub mod commit_tracker;
pub mod ddl_versioning;
pub mod diff;
pub mod hooks;
pub mod webhooks;
use crate::storage::{
BranchId, BranchManager, SnapshotId,
GIT_CONFIG_KEY,
};
use crate::{Error, Result};
use rocksdb::DB;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::sync::Arc;
use parking_lot::RwLock;
pub use config::GitConfig;
pub use link_manager::LinkManager;
pub use commit_tracker::{CommitTracker, CommitState};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GitRepoConfig {
pub repo_path: PathBuf,
pub database_path: String,
pub default_provider: String,
pub auto_sync_enabled: bool,
pub webhook_secret: Option<String>,
pub configured_at: u64,
pub last_sync: Option<u64>,
}
impl Default for GitRepoConfig {
fn default() -> Self {
Self {
repo_path: PathBuf::new(),
database_path: String::new(),
default_provider: "generic".to_string(),
auto_sync_enabled: true,
webhook_secret: None,
configured_at: 0,
last_sync: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DdlHistoryEntry {
pub ddl_id: u64,
pub branch_id: BranchId,
pub lsn: u64,
pub operation: String,
pub object_type: String,
pub object_name: String,
pub ddl_statement: String,
pub executed_at: u64,
pub executed_by: Option<String>,
pub parent_ddl_id: Option<u64>,
pub git_commit: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaSnapshot {
pub name: String,
pub snapshot_id: u64,
pub branch_id: BranchId,
pub schema_ddl: Vec<String>,
pub created_at: u64,
pub comment: Option<String>,
pub git_commit: Option<String>,
pub last_ddl_id: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrBranchInfo {
pub provider: String,
pub pr_number: u64,
pub db_branch_id: BranchId,
pub source_branch: String,
pub target_branch: String,
pub title: Option<String>,
pub created_at: u64,
pub updated_at: u64,
pub status: PrStatus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PrStatus {
Open,
Merged,
Closed,
}
pub struct GitIntegrationManager {
db: Arc<DB>,
branch_manager: Arc<BranchManager>,
config: Arc<RwLock<Option<GitRepoConfig>>>,
link_manager: LinkManager,
commit_tracker: CommitTracker,
timestamp: Arc<RwLock<u64>>,
}
impl GitIntegrationManager {
pub fn new(
db: Arc<DB>,
branch_manager: Arc<BranchManager>,
timestamp: Arc<RwLock<u64>>,
) -> Result<Self> {
let config = Self::load_config(&db)?;
let link_manager = LinkManager::new(Arc::clone(&db), Arc::clone(×tamp));
let commit_tracker = CommitTracker::new(Arc::clone(&db), Arc::clone(×tamp));
Ok(Self {
db,
branch_manager,
config: Arc::new(RwLock::new(config)),
link_manager,
commit_tracker,
timestamp,
})
}
fn load_config(db: &DB) -> Result<Option<GitRepoConfig>> {
match db.get(GIT_CONFIG_KEY) {
Ok(Some(data)) => {
let config: GitRepoConfig = bincode::deserialize(&data)
.map_err(|e| Error::storage(format!("Failed to deserialize Git config: {}", e)))?;
Ok(Some(config))
}
Ok(None) => Ok(None),
Err(e) => Err(Error::storage(format!("Failed to load Git config: {}", e))),
}
}
fn save_config(&self, config: &GitRepoConfig) -> Result<()> {
let data = bincode::serialize(config)
.map_err(|e| Error::storage(format!("Failed to serialize Git config: {}", e)))?;
self.db.put(GIT_CONFIG_KEY, &data)
.map_err(|e| Error::storage(format!("Failed to save Git config: {}", e)))
}
pub fn init(&self, repo_path: PathBuf, database_path: String) -> Result<()> {
let current_ts = *self.timestamp.read();
let config = GitRepoConfig {
repo_path,
database_path,
default_provider: "generic".to_string(),
auto_sync_enabled: true,
webhook_secret: None,
configured_at: current_ts,
last_sync: None,
};
self.save_config(&config)?;
*self.config.write() = Some(config);
Ok(())
}
pub fn is_initialized(&self) -> bool {
self.config.read().is_some()
}
pub fn get_config(&self) -> Option<GitRepoConfig> {
self.config.read().clone()
}
pub fn link_branch(
&self,
git_branch: &str,
db_branch_name: &str,
auto_sync: bool,
) -> Result<()> {
let db_branch = self.branch_manager.get_branch_by_name(db_branch_name)?;
self.link_manager.link(git_branch, db_branch.branch_id, auto_sync)
}
pub fn unlink_branch(&self, git_branch: &str) -> Result<()> {
self.link_manager.unlink(git_branch)
}
pub fn get_linked_branch(&self, git_branch: &str) -> Result<Option<BranchId>> {
self.link_manager.get_linked_branch(git_branch)
}
pub fn record_commit_state(
&self,
commit_sha: &str,
db_branch_id: BranchId,
snapshot_id: SnapshotId,
) -> Result<CommitState> {
self.commit_tracker.record_state(commit_sha, db_branch_id, snapshot_id)
}
pub fn get_commit_state(&self, commit_sha: &str) -> Result<Option<CommitState>> {
self.commit_tracker.get_state(commit_sha)
}
pub fn sync(&self, git_branch: &str) -> Result<Option<BranchId>> {
if let Some(db_branch_id) = self.link_manager.get_linked_branch(git_branch)? {
if let Some(mut config) = self.config.write().take() {
config.last_sync = Some(*self.timestamp.read());
self.save_config(&config)?;
*self.config.write() = Some(config);
}
Ok(Some(db_branch_id))
} else {
Ok(None)
}
}
pub fn list_links(&self) -> Result<Vec<(String, BranchId)>> {
self.link_manager.list_all()
}
pub fn get_status(&self) -> Result<GitIntegrationStatus> {
let config = self.config.read().clone();
let links = self.link_manager.list_all()?;
Ok(GitIntegrationStatus {
initialized: config.is_some(),
config,
linked_branches: links.len(),
links,
})
}
}
#[derive(Debug, Clone)]
pub struct GitIntegrationStatus {
pub initialized: bool,
pub config: Option<GitRepoConfig>,
pub linked_branches: usize,
pub links: Vec<(String, BranchId)>,
}