0.0.2-alpha.2

This commit is contained in:
2026-09-28 16:16:55 +02:00
parent 84c01eb8e8
commit bdbd6a51a4
15 changed files with 1368 additions and 72 deletions

View File

@@ -0,0 +1,379 @@
// file: crates/knowledge-git-lib/src/backend.rs
// version: 1
//! Physical source configuration, generated-state layout and controlled Git process runner.
/// Explicit physical configuration for one controlled Git backend instance.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct GitBackendConfig {
git_executable: std::path::PathBuf,
state_root: std::path::PathBuf,
}
impl crate::GitBackendConfig {
/// Build a backend configuration using `git` from the process environment.
pub fn new(state_root: std::path::PathBuf) -> Result<crate::GitBackendConfig, crate::GitBackendError> {
return crate::GitBackendConfig::with_git_executable(state_root, std::path::PathBuf::from("git"));
}
/// Build a backend configuration with an explicit Git executable path.
pub fn with_git_executable(
state_root: std::path::PathBuf,
git_executable: std::path::PathBuf,
) -> Result<crate::GitBackendConfig, crate::GitBackendError> {
if state_root.as_os_str().is_empty() {
return Err(crate::GitBackendError::EmptyStateRoot);
}
if git_executable.as_os_str().is_empty() {
return Err(crate::GitBackendError::EmptyGitExecutable);
}
return Ok(crate::GitBackendConfig { git_executable, state_root });
}
/// Return the configured Git executable path.
pub fn git_executable(&self) -> &std::path::Path {
return self.git_executable.as_path();
}
/// Return the exclusive generated-state root owned by this backend instance.
pub fn state_root(&self) -> &std::path::Path {
return self.state_root.as_path();
}
/// Return the empty controlled hooks directory used to disable repository hooks.
pub fn hooks_path(&self) -> std::path::PathBuf {
return self.state_root.join("hooks-disabled");
}
/// Return the directory that contains controlled bare mirrors.
pub fn repositories_path(&self) -> std::path::PathBuf {
return self.state_root.join("repositories");
}
/// Return the directory reserved for revision-addressed snapshots.
pub fn snapshots_path(&self) -> std::path::PathBuf {
return self.state_root.join("snapshots");
}
}
/// Coarse physical source kind used by the backend.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GitSourceKind {
/// Source is a path on the local filesystem.
Local,
/// Source is a Git remote location interpreted by the Git executable.
Remote,
}
/// Physical Git source configuration associated with one logical source identifier.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct GitSourceConfig {
location: std::ffi::OsString,
source_id: knowledge_domain_lib::SourceId,
source_kind: crate::GitSourceKind,
}
impl crate::GitSourceConfig {
/// Build a local Git source configuration.
pub fn local(
source_id: knowledge_domain_lib::SourceId,
path: std::path::PathBuf,
) -> Result<crate::GitSourceConfig, crate::GitBackendError> {
if path.as_os_str().is_empty() {
return Err(crate::GitBackendError::EmptySourceLocation);
}
return Ok(crate::GitSourceConfig {
location: path.into_os_string(),
source_id,
source_kind: crate::GitSourceKind::Local,
});
}
/// Build a remote Git source configuration without interpreting provider-specific syntax.
pub fn remote(source_id: knowledge_domain_lib::SourceId, location: &str) -> Result<crate::GitSourceConfig, crate::GitBackendError> {
if location.trim().is_empty() {
return Err(crate::GitBackendError::EmptySourceLocation);
}
return Ok(crate::GitSourceConfig {
location: std::ffi::OsString::from(location),
source_id,
source_kind: crate::GitSourceKind::Remote,
});
}
/// Return the logical source identifier associated with this physical source.
pub fn source_id(&self) -> &knowledge_domain_lib::SourceId {
return &self.source_id;
}
/// Return whether the source is local or remote.
pub fn source_kind(&self) -> crate::GitSourceKind {
return self.source_kind;
}
}
/// Path-safe storage key derived from a logical Knowledge source identifier.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct GitSourceKey(String);
impl crate::GitSourceKey {
/// Derive a reversible collision-free key from the UTF-8 bytes of a logical source identifier.
pub fn from_source_id(source_id: &knowledge_domain_lib::SourceId) -> crate::GitSourceKey {
let mut value = String::from("source-");
for byte in source_id.as_str().as_bytes() {
value.push_str(format!("{:02x}", *byte).as_str());
}
return crate::GitSourceKey(value);
}
/// Return the path-safe key text.
pub fn as_str(&self) -> &str {
return self.0.as_str();
}
}
/// Result of ensuring that a controlled mirror exists.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GitMirrorStatus {
/// The mirror was created by the current call.
Created,
/// A valid existing mirror was reused.
Existing,
}
/// Description of one initialized or already existing controlled mirror.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct GitMirror {
repository_path: std::path::PathBuf,
source_key: crate::GitSourceKey,
status: crate::GitMirrorStatus,
}
impl crate::GitMirror {
/// Return the controlled bare-repository path.
pub fn repository_path(&self) -> &std::path::Path {
return self.repository_path.as_path();
}
/// Return the path-safe key associated with the logical source identifier.
pub fn source_key(&self) -> &crate::GitSourceKey {
return &self.source_key;
}
/// Return whether the mirror was newly created or already existed.
pub fn status(&self) -> crate::GitMirrorStatus {
return self.status;
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct GitCommandSpec {
arguments: Vec<std::ffi::OsString>,
executable: std::path::PathBuf,
hooks_path: std::path::PathBuf,
operation: crate::GitOperation,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct GitCommandOutput {
stdout: Vec<u8>,
}
trait GitCommandRunner {
fn run(&self, specification: &GitCommandSpec) -> Result<GitCommandOutput, crate::GitBackendError>;
}
struct SystemGitRunner;
impl GitCommandRunner for SystemGitRunner {
fn run(&self, specification: &GitCommandSpec) -> Result<GitCommandOutput, crate::GitBackendError> {
let mut command = std::process::Command::new(&specification.executable);
command.args(&specification.arguments);
command.env("GIT_TERMINAL_PROMPT", "0");
command.env_remove("GIT_ALTERNATE_OBJECT_DIRECTORIES");
command.env_remove("GIT_COMMON_DIR");
command.env_remove("GIT_CONFIG_PARAMETERS");
command.env_remove("GIT_DIR");
command.env_remove("GIT_INDEX_FILE");
command.env_remove("GIT_OBJECT_DIRECTORY");
command.env_remove("GIT_WORK_TREE");
command.env("GIT_CONFIG_COUNT", "1");
command.env("GIT_CONFIG_KEY_0", "core.hooksPath");
command.env("GIT_CONFIG_VALUE_0", &specification.hooks_path);
let output_result = command.output();
let output = match output_result {
Ok(value) => value,
Err(_) => {
return Err(crate::GitBackendError::GitUnavailable {
operation: specification.operation,
});
}
};
if !output.status.success() {
return Err(crate::GitBackendError::GitCommandFailed {
operation: specification.operation,
exit_code: output.status.code(),
});
}
return Ok(GitCommandOutput { stdout: output.stdout });
}
}
/// Controlled Git backend that owns local mirror initialization and inspection.
pub struct GitBackend {
config: crate::GitBackendConfig,
runner: Box<dyn GitCommandRunner>,
}
impl crate::GitBackend {
/// Create a backend using the configured real Git executable.
pub fn new(config: crate::GitBackendConfig) -> crate::GitBackend {
return crate::GitBackend {
config,
runner: Box::new(SystemGitRunner),
};
}
#[cfg(test)]
fn with_runner(config: crate::GitBackendConfig, runner: Box<dyn GitCommandRunner>) -> crate::GitBackend {
return crate::GitBackend { config, runner };
}
/// Return the immutable backend configuration.
pub fn config(&self) -> &crate::GitBackendConfig {
return &self.config;
}
/// Derive the controlled mirror path without interpolating the raw source identifier.
pub fn repository_path(&self, source_id: &knowledge_domain_lib::SourceId) -> std::path::PathBuf {
let source_key = crate::GitSourceKey::from_source_id(source_id);
return self.config.repositories_path().join(format!("{}.git", source_key.as_str()));
}
/// Inspect one mirror when it already exists without creating it.
pub fn inspect_mirror(&self, source_id: &knowledge_domain_lib::SourceId) -> Result<Option<crate::GitMirror>, crate::GitBackendError> {
let source_key = crate::GitSourceKey::from_source_id(source_id);
let repository_path = self.repository_path(source_id);
if !repository_path.exists() {
return Ok(None);
}
let inspection_result = self.inspect_bare_repository(&repository_path);
match inspection_result {
Ok(()) => {
return Ok(Some(crate::GitMirror {
repository_path,
source_key,
status: crate::GitMirrorStatus::Existing,
}));
}
Err(error) => return Err(error),
}
}
/// Ensure that one configured source has a valid controlled bare mirror.
pub fn ensure_mirror(&self, source: &crate::GitSourceConfig) -> Result<crate::GitMirror, crate::GitBackendError> {
let preparation_result = self.prepare_state_directories();
match preparation_result {
Ok(()) => (),
Err(error) => return Err(error),
}
let source_key = crate::GitSourceKey::from_source_id(source.source_id());
let repository_path = self.repository_path(source.source_id());
if repository_path.exists() {
let inspection_result = self.inspect_bare_repository(&repository_path);
match inspection_result {
Ok(()) => {
return Ok(crate::GitMirror {
repository_path,
source_key,
status: crate::GitMirrorStatus::Existing,
});
}
Err(error) => return Err(error),
}
}
let clone_result = self.clone_mirror(source, &repository_path);
match clone_result {
Ok(()) => (),
Err(error) => return Err(error),
}
let inspection_result = self.inspect_bare_repository(&repository_path);
match inspection_result {
Ok(()) => {
return Ok(crate::GitMirror {
repository_path,
source_key,
status: crate::GitMirrorStatus::Created,
});
}
Err(error) => return Err(error),
}
}
fn prepare_state_directories(&self) -> Result<(), crate::GitBackendError> {
for path in [self.config.hooks_path(), self.config.repositories_path(), self.config.snapshots_path()] {
let create_result = std::fs::create_dir_all(path);
if create_result.is_err() {
return Err(crate::GitBackendError::StatePreparationFailed);
}
}
return Ok(());
}
fn clone_mirror(&self, source: &crate::GitSourceConfig, repository_path: &std::path::Path) -> Result<(), crate::GitBackendError> {
let mut arguments = vec![std::ffi::OsString::from("clone"), std::ffi::OsString::from("--mirror")];
if source.source_kind == crate::GitSourceKind::Local {
arguments.push(std::ffi::OsString::from("--no-local"));
}
arguments.push(std::ffi::OsString::from("--"));
arguments.push(source.location.clone());
arguments.push(repository_path.as_os_str().to_os_string());
let specification = GitCommandSpec {
arguments,
executable: self.config.git_executable().to_path_buf(),
hooks_path: self.config.hooks_path(),
operation: crate::GitOperation::CloneMirror,
};
let run_result = self.runner.run(&specification);
match run_result {
Ok(_) => return Ok(()),
Err(error) => return Err(error),
}
}
fn inspect_bare_repository(&self, repository_path: &std::path::Path) -> Result<(), crate::GitBackendError> {
let specification = GitCommandSpec {
arguments: vec![
std::ffi::OsString::from("--git-dir"),
repository_path.as_os_str().to_os_string(),
std::ffi::OsString::from("rev-parse"),
std::ffi::OsString::from("--is-bare-repository"),
],
executable: self.config.git_executable().to_path_buf(),
hooks_path: self.config.hooks_path(),
operation: crate::GitOperation::InspectMirror,
};
let run_result = self.runner.run(&specification);
let output = match run_result {
Ok(value) => value,
Err(error) => return Err(error),
};
let stdout_result = std::str::from_utf8(&output.stdout);
let stdout = match stdout_result {
Ok(value) => value.trim(),
Err(_) => {
return Err(crate::GitBackendError::UnexpectedGitOutput {
operation: crate::GitOperation::InspectMirror,
});
}
};
if stdout != "true" {
return Err(crate::GitBackendError::InvalidMirror);
}
return Ok(());
}
}
#[cfg(test)]
#[path = "../unit_tests/backend.rs"]
mod tests;