use super::{IndexError, PackageIndex, PackageMeta, VersionMeta};
use crate::cache;
use std::time::Duration;
const CACHE_TTL: Duration = Duration::from_secs(60 * 60);
pub struct Hunter;
impl Hunter {
const DEFAULT_CMAKE_URL: &'static str =
"https://raw.githubusercontent.com/cpp-pm/hunter/master/cmake/configs/default.cmake";
fn parse_packages(content: &str) -> Vec<PackageMeta> {
let mut packages = Vec::new();
for line in content.lines() {
let line = line.trim();
if !line.starts_with("hunter_default_version(") {
continue;
}
let start = match line.find('(') {
Some(i) => i + 1,
None => continue,
};
let end = match line.rfind(')') {
Some(i) => i,
None => continue,
};
let inner = &line[start..end];
let parts: Vec<&str> = inner.split_whitespace().collect();
if parts.len() < 3 {
continue;
}
let name = parts[0];
let version_idx = parts.iter().position(|&p| p == "VERSION");
let version = match version_idx {
Some(idx) if idx + 1 < parts.len() => parts[idx + 1],
_ => continue,
};
packages.push(PackageMeta {
name: name.to_string(),
version: version.to_string(),
description: None, homepage: Some(format!(
"https://hunter.readthedocs.io/en/latest/packages/pkg/{}.html",
name
)),
repository: Some("https://github.com/cpp-pm/hunter".to_string()),
license: None,
binaries: Vec::new(),
keywords: Vec::new(),
maintainers: Vec::new(),
published: None,
downloads: None,
archive_url: None,
checksum: None,
extra: Default::default(),
});
}
packages
}
fn load_packages() -> Result<Vec<PackageMeta>, IndexError> {
let (data, _was_cached) = cache::fetch_with_cache(
"hunter",
"default-cmake",
Self::DEFAULT_CMAKE_URL,
CACHE_TTL,
)
.map_err(IndexError::Network)?;
let content = String::from_utf8(data)
.map_err(|e| IndexError::Parse(format!("UTF-8 error: {}", e)))?;
Ok(Self::parse_packages(&content))
}
}
impl PackageIndex for Hunter {
fn ecosystem(&self) -> &'static str {
"hunter"
}
fn display_name(&self) -> &'static str {
"Hunter"
}
fn fetch(&self, name: &str) -> Result<PackageMeta, IndexError> {
let packages = Self::load_packages()?;
packages
.into_iter()
.find(|p| p.name.eq_ignore_ascii_case(name))
.ok_or_else(|| IndexError::NotFound(name.to_string()))
}
fn fetch_versions(&self, name: &str) -> Result<Vec<VersionMeta>, IndexError> {
let pkg = self.fetch(name)?;
Ok(vec![VersionMeta {
version: pkg.version,
released: None,
yanked: false,
}])
}
fn search(&self, query: &str) -> Result<Vec<PackageMeta>, IndexError> {
let packages = Self::load_packages()?;
let query_lower = query.to_lowercase();
Ok(packages
.into_iter()
.filter(|p| p.name.to_lowercase().contains(&query_lower))
.take(50)
.collect())
}
fn fetch_all(&self) -> Result<Vec<PackageMeta>, IndexError> {
Self::load_packages()
}
}