use rattler_git::{GitUrl as RattlerGitUrl, git::GitReference as RattlerGitReference};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Checksum {
Sha256(Vec<u8>),
Md5(Vec<u8>),
}
impl Checksum {
pub fn to_hex(&self) -> String {
match self {
Checksum::Sha256(bytes) => hex::encode(bytes),
Checksum::Md5(bytes) => hex::encode(bytes),
}
}
pub fn validate(&self, path: &std::path::Path) -> Result<(), ChecksumMismatch> {
use md5::{Digest, Md5};
use sha2::Sha256;
use std::io::Read;
let mut file = std::fs::File::open(path).map_err(|e| ChecksumMismatch {
expected: self.to_hex(),
actual: format!("<failed to open file: {e}>"),
kind: self.kind(),
})?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)
.map_err(|e| ChecksumMismatch {
expected: self.to_hex(),
actual: format!("<failed to read file: {e}>"),
kind: self.kind(),
})?;
let actual_hex = match self {
Checksum::Sha256(_) => {
let mut hasher = Sha256::new();
hasher.update(&buffer);
hex::encode(hasher.finalize())
}
Checksum::Md5(_) => {
let mut hasher = Md5::new();
hasher.update(&buffer);
hex::encode(hasher.finalize())
}
};
if actual_hex == self.to_hex() {
Ok(())
} else {
Err(ChecksumMismatch {
expected: self.to_hex(),
actual: actual_hex,
kind: self.kind(),
})
}
}
pub fn kind(&self) -> ChecksumKind {
match self {
Checksum::Sha256(_) => ChecksumKind::Sha256,
Checksum::Md5(_) => ChecksumKind::Md5,
}
}
pub fn from_hex_str(value: &str, kind: ChecksumKind) -> Result<Self, hex::FromHexError> {
let bytes = hex::decode(value)?;
Ok(match kind {
ChecksumKind::Sha256 => Checksum::Sha256(bytes),
ChecksumKind::Md5 => Checksum::Md5(bytes),
})
}
}
#[derive(Debug, Clone, Copy)]
pub enum ChecksumKind {
Sha256,
Md5,
}
impl std::fmt::Display for ChecksumKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ChecksumKind::Sha256 => write!(f, "sha256"),
ChecksumKind::Md5 => write!(f, "md5"),
}
}
}
#[derive(Debug, Clone)]
pub struct ChecksumMismatch {
pub expected: String,
pub actual: String,
pub kind: ChecksumKind,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GitSource {
pub url: url::Url,
pub reference: RattlerGitReference,
pub depth: Option<i32>,
pub lfs: bool,
#[serde(default = "default_submodules")]
pub submodules: bool,
pub expected_commit: Option<String>,
}
fn default_submodules() -> bool {
true
}
impl GitSource {
pub fn to_git_url(&self) -> RattlerGitUrl {
RattlerGitUrl::from_reference(self.url.clone(), self.reference.clone())
}
pub fn new(
url: url::Url,
reference: RattlerGitReference,
depth: Option<i32>,
lfs: bool,
submodules: bool,
) -> Self {
Self {
url,
reference,
depth,
lfs,
submodules,
expected_commit: None,
}
}
pub fn with_expected_commit(
url: url::Url,
reference: RattlerGitReference,
depth: Option<i32>,
lfs: bool,
submodules: bool,
expected_commit: Option<String>,
) -> Self {
Self {
url,
reference,
depth,
lfs,
submodules,
expected_commit,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IdentityCheck {
pub identity: String,
pub issuer: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AttestationVerification {
pub bundle_url: Option<url::Url>,
pub identity_checks: Vec<IdentityCheck>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UrlSource {
pub urls: Vec<url::Url>,
pub checksums: Vec<Checksum>,
pub file_name: Option<String>,
pub attestation: Option<AttestationVerification>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Source {
Git(GitSource),
Url(UrlSource),
Path(PathBuf), }