use microsandbox::{MicrosandboxError, Sandbox};
use std::time::Duration;
use thiserror::Error;
const CONFIG_TIMEOUT: Duration = Duration::from_secs(5);
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct GitIdentity<'a> {
pub name: Option<&'a str>,
pub email: Option<&'a str>,
}
impl<'a> GitIdentity<'a> {
fn entries(self) -> impl Iterator<Item = (&'static str, &'a str)> {
[("user.name", self.name), ("user.email", self.email)]
.into_iter()
.filter_map(|(key, value)| Some((key, value?)))
}
}
#[derive(Error, Debug)]
enum SetError {
#[error(transparent)]
Exec(#[from] MicrosandboxError),
#[error("git config exited with code {code}: {stderr}")]
Exited {
code: i32,
stderr: String,
},
}
pub async fn apply(sb: &Sandbox, identity: GitIdentity<'_>) {
for (key, value) in identity.entries() {
if let Err(err) = set(sb, key, value).await {
tracing::warn!("failed to set git {key} in sandbox {}: {err}", sb.name());
}
}
}
async fn set(sb: &Sandbox, key: &str, value: &str) -> Result<(), SetError> {
let output = sb
.exec_with("git", |exec| {
exec.args(["config", "--global", key, value])
.cwd("/")
.timeout(CONFIG_TIMEOUT)
})
.await?;
let status = output.status();
if status.success {
return Ok(());
}
Err(SetError::Exited {
code: status.code,
stderr: String::from_utf8_lossy(output.stderr_bytes())
.trim()
.to_string(),
})
}