mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 13:20:42 +00:00
Refactor: Introduce resolve_packages for wildcard handling and enhance repository package resolution
- Added `resolve_packages` to centralize wildcard pattern processing and match the latest package versions consistently. - Updated both `FileBackend` and `RestBackend` to support wildcard patterns via `glob_to_regex` logic. - Made `glob_to_regex` public to facilitate regex conversion for glob patterns. - Improved `catalog_manager` handling in `RestBackend` by explicitly loading catalog parts to ensure accurate package matching. - Replaced redundant FMRI parsing logic with `resolve_packages` for cleaner and more maintainable code.
This commit is contained in:
parent
f8068364bc
commit
e4f49cd7c8
3 changed files with 74 additions and 20 deletions
|
|
@ -120,7 +120,7 @@ fn parse_query(query: &str) -> SearchQuery {
|
|||
}
|
||||
}
|
||||
|
||||
fn glob_to_regex(pattern: &str) -> String {
|
||||
pub fn glob_to_regex(pattern: &str) -> String {
|
||||
let mut regex = String::from("^");
|
||||
for c in pattern.chars() {
|
||||
match c {
|
||||
|
|
@ -1000,7 +1000,7 @@ impl ReadableRepository for FileBackend {
|
|||
self.find_manifests_recursive(
|
||||
&publisher_pkg_dir,
|
||||
&pub_name,
|
||||
pattern,
|
||||
pattern.map(glob_to_regex).as_deref(),
|
||||
&mut packages,
|
||||
)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1218,7 +1218,7 @@ impl RestBackend {
|
|||
.join(&pub_name)
|
||||
};
|
||||
|
||||
let catalog_manager = self.get_catalog_manager(&pub_name)?;
|
||||
let mut catalog_manager = self.get_catalog_manager(&pub_name)?;
|
||||
|
||||
let attrs_path = cache_path.join("catalog.attrs");
|
||||
let attrs_content = fs::read_to_string(&attrs_path).map_err(|e| {
|
||||
|
|
@ -1238,6 +1238,11 @@ impl RestBackend {
|
|||
let mut seen_fmris = HashSet::new();
|
||||
|
||||
for part_name in parts.keys() {
|
||||
// Load part explicitly because CatalogManager doesn't load them automatically
|
||||
catalog_manager.load_part(part_name).map_err(|e| {
|
||||
RepositoryError::Other(format!("Failed to load catalog part {}: {}", part_name, e))
|
||||
})?;
|
||||
|
||||
if let Some(part) = catalog_manager.get_part(part_name) {
|
||||
// Match stems against pattern
|
||||
for (publisher_in_catalog, stems) in &part.packages {
|
||||
|
|
@ -1248,16 +1253,13 @@ impl RestBackend {
|
|||
for (stem, versions) in stems {
|
||||
let matches = if pattern == "*" {
|
||||
true
|
||||
} else if pattern.contains('*') {
|
||||
// Basic glob matching (stem matching pattern)
|
||||
let re_pattern = pattern.replace('*', ".*");
|
||||
if let Ok(re) = regex::Regex::new(&format!("^{}$", re_pattern)) {
|
||||
} else {
|
||||
let re_str = super::file_backend::glob_to_regex(pattern);
|
||||
if let Ok(re) = regex::Regex::new(&re_str) {
|
||||
re.is_match(stem)
|
||||
} else {
|
||||
stem == pattern
|
||||
}
|
||||
} else {
|
||||
stem == pattern
|
||||
};
|
||||
|
||||
if matches {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use libips::repository::{
|
|||
};
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use std::path::PathBuf;
|
||||
use tracing::info;
|
||||
use tracing::{info, warn};
|
||||
use tracing_subscriber::{EnvFilter, fmt};
|
||||
|
||||
struct ConsoleProgressReporter;
|
||||
|
|
@ -55,22 +55,15 @@ fn main() -> Result<()> {
|
|||
|
||||
let cli = Cli::parse();
|
||||
|
||||
// Open destination repository
|
||||
// We'll open it inside each branch to avoid borrow checker issues with moves
|
||||
|
||||
let fmris: Vec<Fmri> = cli
|
||||
.packages
|
||||
.iter()
|
||||
.map(|s| Fmri::parse(s))
|
||||
.collect::<std::result::Result<Vec<_>, _>>()
|
||||
.into_diagnostic()?;
|
||||
|
||||
let progress = ConsoleProgressReporter;
|
||||
|
||||
// Determine if source is a URL or a path and receive packages
|
||||
if cli.source.starts_with("http://") || cli.source.starts_with("https://") {
|
||||
let source_repo = RestBackend::open(&cli.source).into_diagnostic()?;
|
||||
let dest_repo = FileBackend::open(&cli.dest).into_diagnostic()?;
|
||||
|
||||
let fmris = resolve_packages(&source_repo, cli.publisher.as_deref(), &cli.packages)?;
|
||||
|
||||
let mut receiver = PackageReceiver::new(&source_repo, dest_repo);
|
||||
receiver = receiver.with_progress(&progress);
|
||||
receiver
|
||||
|
|
@ -79,6 +72,9 @@ fn main() -> Result<()> {
|
|||
} else {
|
||||
let source_repo = FileBackend::open(&cli.source).into_diagnostic()?;
|
||||
let dest_repo = FileBackend::open(&cli.dest).into_diagnostic()?;
|
||||
|
||||
let fmris = resolve_packages(&source_repo, cli.publisher.as_deref(), &cli.packages)?;
|
||||
|
||||
let mut receiver = PackageReceiver::new(&source_repo, dest_repo);
|
||||
receiver = receiver.with_progress(&progress);
|
||||
receiver
|
||||
|
|
@ -90,3 +86,59 @@ fn main() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_packages<R: ReadableRepository>(
|
||||
repo: &R,
|
||||
default_publisher: Option<&str>,
|
||||
packages: &[String],
|
||||
) -> Result<Vec<Fmri>> {
|
||||
let mut resolved_fmris = Vec::new();
|
||||
|
||||
for pkg_str in packages {
|
||||
if pkg_str.contains('*') || pkg_str.contains('?') {
|
||||
// It's a pattern, resolve it
|
||||
info!("Resolving wildcard pattern: {}", pkg_str);
|
||||
let matched = repo.list_packages(default_publisher, Some(pkg_str)).into_diagnostic()?;
|
||||
|
||||
if matched.is_empty() {
|
||||
warn!("No packages matched pattern: {}", pkg_str);
|
||||
}
|
||||
|
||||
// For each matched stem, we probably want the newest version if not specified.
|
||||
// list_packages returns all versions. PackageReceiver::receive also handles
|
||||
// FMRIs without versions by picking the newest.
|
||||
// But list_packages returns full FMRIs. If the pattern matched multiple packages,
|
||||
// we get all versions of all of them.
|
||||
|
||||
// To be consistent with IPS, if someone says "text/*", they usually want
|
||||
// the latest version of everything that matches.
|
||||
|
||||
let mut latest_versions: std::collections::HashMap<String, Fmri> = std::collections::HashMap::new();
|
||||
|
||||
for pi in matched {
|
||||
let entry = latest_versions.entry(pi.fmri.name.clone());
|
||||
match entry {
|
||||
std::collections::hash_map::Entry::Occupied(mut oe) => {
|
||||
if pi.fmri.version() > oe.get().version() {
|
||||
oe.insert(pi.fmri);
|
||||
}
|
||||
}
|
||||
std::collections::hash_map::Entry::Vacant(ve) => {
|
||||
ve.insert(pi.fmri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (_, fmri) in latest_versions {
|
||||
info!("Found package: {}", fmri);
|
||||
resolved_fmris.push(fmri);
|
||||
}
|
||||
} else {
|
||||
// It's a regular FMRI or package name
|
||||
let fmri = Fmri::parse(pkg_str).into_diagnostic()?;
|
||||
resolved_fmris.push(fmri);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(resolved_fmris)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue