use std::path::{Path, PathBuf};
use cljrs_project::config::TrustedSigner;
#[derive(Debug)]
pub enum SignatureFailure {
Untrusted { commit: String, reason: String },
Error(String),
}
pub trait VcsProvider: Send + Sync {
fn find_repo_root(&self, start: &Path) -> Option<PathBuf>;
fn file_at_commit(
&self,
repo_root: &Path,
rel_path: &str,
commit: &str,
) -> Result<String, String>;
fn verify_commit_signature(
&self,
repo_root: &Path,
commit: &str,
) -> Result<(), SignatureFailure>;
fn load_trusted_signers(&self, signers: &[TrustedSigner]) -> usize;
}
pub fn default_provider() -> Option<std::sync::Arc<dyn VcsProvider>> {
#[cfg(all(feature = "deps", not(target_arch = "wasm32")))]
{
Some(std::sync::Arc::new(ProjectVcs::new()))
}
#[cfg(not(all(feature = "deps", not(target_arch = "wasm32"))))]
{
None
}
}
#[cfg(all(feature = "deps", not(target_arch = "wasm32")))]
pub struct ProjectVcs {
trusted: std::sync::RwLock<std::sync::Arc<cljrs_project::vcs::TrustedKeys>>,
}
#[cfg(all(feature = "deps", not(target_arch = "wasm32")))]
impl Default for ProjectVcs {
fn default() -> Self {
Self::new()
}
}
#[cfg(all(feature = "deps", not(target_arch = "wasm32")))]
impl ProjectVcs {
pub fn new() -> Self {
Self {
trusted: std::sync::RwLock::new(std::sync::Arc::new(
cljrs_project::vcs::TrustedKeys::new(),
)),
}
}
}
#[cfg(all(feature = "deps", not(target_arch = "wasm32")))]
impl VcsProvider for ProjectVcs {
fn find_repo_root(&self, start: &Path) -> Option<PathBuf> {
cljrs_project::vcs::find_repo_root(start)
}
fn file_at_commit(
&self,
repo_root: &Path,
rel_path: &str,
commit: &str,
) -> Result<String, String> {
cljrs_project::vcs::get_file_at_commit(repo_root, rel_path, commit)
.map_err(|e| e.to_string())
}
fn verify_commit_signature(
&self,
repo_root: &Path,
commit: &str,
) -> Result<(), SignatureFailure> {
let trusted = self.trusted.read().unwrap().clone();
cljrs_project::vcs::verify_commit_signature(repo_root, commit, &trusted).map_err(
|e| match e {
cljrs_project::vcs::VcsError::SignatureVerificationFailed { commit, reason } => {
SignatureFailure::Untrusted { commit, reason }
}
other => SignatureFailure::Error(other.to_string()),
},
)
}
fn load_trusted_signers(&self, signers: &[TrustedSigner]) -> usize {
let mut keys = cljrs_project::vcs::TrustedKeys::new();
let mut loaded = 0usize;
for signer in signers {
let result = match signer {
TrustedSigner::Inline(text) => keys.add_key_text(text),
TrustedSigner::File(path) => match std::fs::read_to_string(path) {
Ok(text) => keys.add_key_text(&text),
Err(e) => {
eprintln!(
"cljrs: warning: could not read trusted signer key {}: {e}",
path.display()
);
continue;
}
},
};
match result {
Ok(()) => loaded += 1,
Err(e) => eprintln!("cljrs: warning: invalid trusted signer key: {e}"),
}
}
*self.trusted.write().unwrap() = std::sync::Arc::new(keys);
loaded
}
}