text/x-rust
•
6.49 KB
•
204 lines
//! Repository service
//!
//! Provides a RepoService struct that wraps rubhub-repo functions,
//! handling event emission for repository changes.
use std::path::PathBuf;
use std::sync::Arc;
use anyhow::Result;
use time::OffsetDateTime;
use tokio::sync::broadcast;
use crate::{RepoEvent, RepoEventInfo};
use rubhub_auth_store::AuthStore;
// Re-export types from rubhub-repo for convenience
pub use rubhub_repo::{CommitParams, EntryKind, GitRefInfo, GitSummary, GitTreeEntry};
/// Newtype wrapper to implement AuthorResolver for AuthStore
struct AuthStoreResolver<'a>(&'a AuthStore);
impl rubhub_repo::AuthorResolver for AuthStoreResolver<'_> {
fn resolve_email(&self, email: &str) -> Option<String> {
self.0.get_user_slug_by_email(email)
}
}
/// Repository service that handles git operations with event emission
#[derive(Debug, Clone)]
pub struct RepoService {
git_root: PathBuf,
event_tx: broadcast::Sender<RepoEvent>,
auth: Arc<AuthStore>,
}
impl RepoService {
/// Create a new RepoService
pub fn new(
git_root: PathBuf,
event_tx: broadcast::Sender<RepoEvent>,
auth: Arc<AuthStore>,
) -> Self {
Self {
git_root,
event_tx,
auth,
}
}
/// Emit a repository event to all SSE listeners
fn emit_event(&self, event: RepoEvent) {
let _ = self.event_tx.send(event);
}
/// Emit events for repository changes
fn emit_changes(&self, before: &GitSummary, after: &GitSummary) {
let timestamp = OffsetDateTime::now_utc();
let (branch_changes, tag_changes) = before.diff(after);
for (branch, commit_hash) in branch_changes {
self.emit_event(RepoEvent::BranchUpdated {
info: RepoEventInfo {
owner: after.owner().to_string(),
project: after.project().to_string(),
commit_hash,
timestamp,
},
branch,
});
}
for (tag, commit_hash) in tag_changes {
self.emit_event(RepoEvent::TagUpdated {
info: RepoEventInfo {
owner: after.owner().to_string(),
project: after.project().to_string(),
commit_hash,
timestamp,
},
tag,
});
}
}
/// Execute an operation with event emission for repository changes
async fn with_event_emission<F, Fut>(
&self,
user_name: &str,
project_slug: &str,
operation: F,
) -> Result<()>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = Result<()>>,
{
let before = rubhub_repo::capture_git_summary(&self.git_root, user_name, project_slug);
operation().await?;
let after = rubhub_repo::capture_git_summary(&self.git_root, user_name, project_slug);
self.emit_changes(&before, &after);
Ok(())
}
/// Create a new bare git repository
pub async fn create_bare_repo(&self, user: &str, project: &str) -> Result<(), std::io::Error> {
rubhub_repo::create_bare_repo(&self.git_root, user, project)
.await
.map_err(|e| std::io::Error::other(e.to_string()))
}
/// Set the HEAD reference for a repository
pub async fn set_git_head(
&self,
user_name: &str,
project_slug: &str,
head_branch: &str,
) -> Result<()> {
rubhub_repo::set_git_head(&self.git_root, user_name, project_slug, head_branch).await
}
/// Get a summary of a repository's branches and tags
pub async fn get_git_summary(&self, user_name: &str, project_slug: &str) -> Option<GitSummary> {
rubhub_repo::get_git_summary(&self.git_root, user_name, project_slug).await
}
/// Get information about a git reference (branch/tag) with commits
pub async fn get_git_info(
&self,
user_name: &str,
project_slug: &str,
branch: &str,
max_commits: i32,
offset: i32,
) -> Option<GitRefInfo> {
rubhub_repo::get_git_info(
&self.git_root,
user_name,
project_slug,
branch,
max_commits,
offset,
&AuthStoreResolver(self.auth.as_ref()),
)
.await
}
/// Get the contents of a file from a repository
pub async fn get_git_file(
&self,
user_name: &str,
project_slug: &str,
branch: &str,
path: &str,
) -> Result<Vec<u8>> {
rubhub_repo::get_git_file(&self.git_root, user_name, project_slug, branch, path).await
}
/// Get the entries in a directory from a repository
pub async fn get_git_tree(
&self,
user_name: &str,
project_slug: &str,
branch: &str,
path: &str,
) -> Result<Vec<GitTreeEntry>> {
rubhub_repo::get_git_tree(&self.git_root, user_name, project_slug, branch, path).await
}
/// Check if a branch exists in a repository
pub async fn branch_exists(&self, user_name: &str, project_slug: &str, branch: &str) -> bool {
rubhub_repo::branch_exists(&self.git_root, user_name, project_slug, branch).await
}
/// Capture git summary for use with emit_repo_changes
pub fn capture_summary(&self, owner: &str, project: &str) -> GitSummary {
rubhub_repo::capture_git_summary(&self.git_root, owner, project)
}
/// Emit repository change events by comparing before/after states
pub fn emit_repo_changes(&self, before: &GitSummary, after: &GitSummary) {
self.emit_changes(before, after);
}
/// Create an orphan branch with an initial commit containing one file
pub async fn create_orphan_branch(&self, params: CommitParams<'_>) -> Result<()> {
let user_name = params.user_name;
let project_slug = params.project_slug;
let git_root = self.git_root.clone();
self.with_event_emission(user_name, project_slug, || async {
rubhub_repo::create_orphan_branch(&git_root, params).await
})
.await
}
/// Add a file to a branch and create a commit
pub async fn add_file_to_branch(&self, params: CommitParams<'_>) -> Result<()> {
let user_name = params.user_name;
let project_slug = params.project_slug;
let git_root = self.git_root.clone();
self.with_event_emission(user_name, project_slug, || async {
rubhub_repo::add_file_to_branch(&git_root, params).await
})
.await
}
}