mod conversion;
mod metadata_cache;
mod models;
mod storage;
#[cfg(test)]
mod tests;
use chrono::Utc;
use log::warn;
use std::str::FromStr;
use crate::config::KopiConfig;
use crate::error::{KopiError, Result};
use crate::metadata::provider::MetadataProvider;
use crate::models::distribution::Distribution as JdkDistribution;
use crate::models::metadata::JdkMetadata;
use crate::models::package::ChecksumType;
pub use models::{PlatformFilter, SearchResult, VersionSearchType};
pub use metadata_cache::{DistributionCache, MetadataCache};
pub use crate::platform::{get_current_architecture, get_current_os, get_current_platform};
pub use conversion::{
convert_api_to_cache, convert_package_to_jdk_metadata, parse_architecture_from_filename,
};
pub use storage::{load_cache, save_cache};
pub fn get_metadata(requested_version: Option<&str>, config: &KopiConfig) -> Result<MetadataCache> {
let cache_path = config.metadata_cache_path()?;
if cache_path.exists() {
match load_cache(&cache_path) {
Ok(loaded_cache) => {
if let Some(version) = requested_version
&& !loaded_cache.has_version(version)
{
return fetch_and_cache_metadata(false, config);
}
return Ok(loaded_cache);
}
Err(e) => {
warn!("Failed to load cache: {e}. Falling back to API.");
}
}
}
fetch_and_cache_metadata(false, config)
}
pub fn fetch_and_cache_metadata(
javafx_bundled: bool,
config: &KopiConfig,
) -> Result<MetadataCache> {
let provider = MetadataProvider::from_config(config)?;
let mut metadata = provider
.fetch_all()
.map_err(|e| KopiError::MetadataFetch(format!("Failed to fetch metadata from API: {e}")))?;
if javafx_bundled {
metadata.retain(|jdk| jdk.javafx_bundled);
}
let mut new_cache = MetadataCache::new();
let mut distributions: std::collections::HashMap<String, Vec<JdkMetadata>> =
std::collections::HashMap::new();
for jdk in metadata {
distributions
.entry(jdk.distribution.clone())
.or_default()
.push(jdk);
}
for (dist_name, packages) in distributions {
let dist_cache = DistributionCache {
distribution: JdkDistribution::from_str(&dist_name)
.unwrap_or(JdkDistribution::Other(dist_name.clone())),
display_name: dist_name.clone(), packages,
};
new_cache.distributions.insert(dist_name, dist_cache);
}
new_cache.last_updated = Utc::now();
let cache_path = config.metadata_cache_path()?;
new_cache.save(&cache_path)?;
Ok(new_cache)
}
pub fn fetch_and_cache_distribution(
distribution_name: &str,
javafx_bundled: bool,
config: &KopiConfig,
) -> Result<MetadataCache> {
let cache_path = config.metadata_cache_path()?;
let mut result_cache = if cache_path.exists() {
load_cache(&cache_path)?
} else {
MetadataCache::new()
};
let provider = MetadataProvider::from_config(config)?;
let mut packages = provider
.fetch_distribution(distribution_name)
.map_err(|e| {
KopiError::MetadataFetch(format!(
"Failed to fetch packages for {distribution_name}: {e}"
))
})?;
if javafx_bundled {
packages.retain(|jdk| jdk.javafx_bundled);
}
let dist_cache = DistributionCache {
distribution: JdkDistribution::from_str(distribution_name)
.unwrap_or(JdkDistribution::Other(distribution_name.to_string())),
display_name: distribution_name.to_string(), packages,
};
result_cache
.distributions
.insert(distribution_name.to_string(), dist_cache);
result_cache.last_updated = Utc::now();
result_cache.save(&cache_path)?;
Ok(result_cache)
}
pub fn fetch_package_checksum(
package_id: &str,
config: &KopiConfig,
) -> Result<(String, ChecksumType)> {
let cache = get_metadata(None, config)?;
let mut found_metadata = None;
for dist_cache in cache.distributions.values() {
if let Some(metadata) = dist_cache.packages.iter().find(|pkg| pkg.id == package_id) {
found_metadata = Some(metadata.clone());
break;
}
}
let mut metadata = found_metadata.ok_or_else(|| {
KopiError::MetadataFetch(format!(
"Package with ID '{package_id}' not found in cache. Cannot fetch checksum."
))
})?;
if !metadata.is_complete() {
let provider = MetadataProvider::from_config(config)?;
provider.ensure_complete(&mut metadata).map_err(|e| {
KopiError::MetadataFetch(format!("Failed to fetch package checksum: {e}"))
})?;
}
let checksum = metadata.checksum.ok_or_else(|| {
KopiError::MetadataFetch(format!(
"No checksum available for package ID: {package_id}"
))
})?;
let checksum_type = metadata.checksum_type.unwrap_or_else(|| {
warn!("No checksum type available for package ID: {package_id}. Defaulting to SHA256.");
ChecksumType::Sha256
});
Ok((checksum, checksum_type))
}