use anyhow::Result;
use chrono::Utc;
use rama::telemetry::tracing::{debug, warn};
use vein_adapter::{
CacheBackendKind, GemVersion, VersionStatus, calculate_availability, is_version_available,
};
use crate::config::DelayPolicyConfig;
pub async fn record_new_version(
config: &DelayPolicyConfig,
index: &CacheBackendKind,
name: &str,
version: &str,
platform: Option<&str>,
sha256: &str,
) -> Result<()> {
if !config.enabled {
return Ok(());
}
if let Ok(Some(_existing)) = index.get_gem_version(name, version, platform).await {
return Ok(());
}
let delay_days = config.delay_for_gem(name);
let now = Utc::now();
let available_after = calculate_availability(now, &config.to_adapter_policy());
let status = if delay_days == 0 || config.is_pinned(name, version) {
VersionStatus::Pinned
} else {
VersionStatus::Quarantine
};
let status_reason = if status == VersionStatus::Pinned {
config
.pin_reason(name, version)
.map(|r| format!("pinned: {}", r))
.or_else(|| Some("zero-delay gem".to_string()))
} else {
Some("auto".to_string())
};
let gem_version = GemVersion {
id: 0,
name: name.to_string(),
version: version.to_string(),
platform: platform.map(String::from),
sha256: Some(sha256.to_string()),
published_at: now,
available_after,
status,
status_reason,
upstream_yanked: false,
created_at: now,
updated_at: now,
};
index.upsert_gem_version(&gem_version).await?;
debug!(
gem = %name,
version = %version,
status = %status,
available_after = %available_after,
"Recorded new gem version in quarantine system"
);
Ok(())
}
pub async fn filter_compact_info(
config: &DelayPolicyConfig,
index: &CacheBackendKind,
gem_name: &str,
body: &[u8],
) -> Result<Vec<u8>> {
if !config.enabled {
return Ok(body.to_vec());
}
let body_str = match std::str::from_utf8(body) {
Ok(s) => s,
Err(_) => return Ok(body.to_vec()), };
let now = Utc::now();
let versions = match index.get_gem_versions_for_index(gem_name).await {
Ok(v) => v,
Err(err) => {
warn!(
error = %err,
gem = %gem_name,
"Failed to fetch quarantine status, passing through unfiltered"
);
return Ok(body.to_vec());
}
};
if versions.is_empty() {
return Ok(body.to_vec());
}
let quarantined: std::collections::HashSet<String> = versions
.iter()
.filter(|v| !is_version_available(v, now))
.map(|v| format_version_key(&v.version, v.platform.as_deref()))
.collect();
if quarantined.is_empty() {
return Ok(body.to_vec()); }
let mut output_lines = Vec::new();
for line in body_str.lines() {
if line == "---" || line.is_empty() {
output_lines.push(line);
continue;
}
if let Some((version_key, _rest)) = parse_compact_line(line)
&& quarantined.contains(&version_key)
{
debug!(
gem = %gem_name,
version_key = %version_key,
"Filtering quarantined version from compact index"
);
continue; }
output_lines.push(line);
}
Ok(output_lines.join("\n").into_bytes())
}
fn parse_compact_line(line: &str) -> Option<(String, &str)> {
let pipe_pos = line.find('|')?;
let version_part = line[..pipe_pos].trim();
let rest = &line[pipe_pos..];
let mut parts = version_part.split_whitespace();
let version = parts.next()?;
let platform = parts.next();
let key = format_version_key(version, platform);
Some((key, rest))
}
fn format_version_key(version: &str, platform: Option<&str>) -> String {
match platform {
Some(p) if p != "ruby" => format!("{}:{}", version, p),
_ => version.to_string(),
}
}
#[allow(dead_code, clippy::unused_async)]
pub async fn filter_compact_versions(
config: &DelayPolicyConfig,
_index: &CacheBackendKind,
body: &[u8],
) -> Result<Vec<u8>> {
if !config.enabled {
return Ok(body.to_vec());
}
Ok(body.to_vec())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_compact_line_simple() {
let (key, rest) = parse_compact_line("1.0.0 |abc123|dep1").unwrap();
assert_eq!(key, "1.0.0");
assert_eq!(rest, "|abc123|dep1");
}
#[test]
fn test_parse_compact_line_with_platform() {
let (key, rest) = parse_compact_line("1.0.0 x86_64-linux|abc123|dep1").unwrap();
assert_eq!(key, "1.0.0:x86_64-linux");
assert_eq!(rest, "|abc123|dep1");
}
#[test]
fn test_parse_compact_line_ruby_platform() {
let (key, _) = parse_compact_line("1.0.0 ruby|abc123|").unwrap();
assert_eq!(key, "1.0.0"); }
#[test]
fn test_format_version_key() {
assert_eq!(format_version_key("1.0.0", None), "1.0.0");
assert_eq!(format_version_key("1.0.0", Some("ruby")), "1.0.0");
assert_eq!(
format_version_key("1.0.0", Some("x86_64-linux")),
"1.0.0:x86_64-linux"
);
}
}