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("^");
|
let mut regex = String::from("^");
|
||||||
for c in pattern.chars() {
|
for c in pattern.chars() {
|
||||||
match c {
|
match c {
|
||||||
|
|
@ -1000,7 +1000,7 @@ impl ReadableRepository for FileBackend {
|
||||||
self.find_manifests_recursive(
|
self.find_manifests_recursive(
|
||||||
&publisher_pkg_dir,
|
&publisher_pkg_dir,
|
||||||
&pub_name,
|
&pub_name,
|
||||||
pattern,
|
pattern.map(glob_to_regex).as_deref(),
|
||||||
&mut packages,
|
&mut packages,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1218,7 +1218,7 @@ impl RestBackend {
|
||||||
.join(&pub_name)
|
.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_path = cache_path.join("catalog.attrs");
|
||||||
let attrs_content = fs::read_to_string(&attrs_path).map_err(|e| {
|
let attrs_content = fs::read_to_string(&attrs_path).map_err(|e| {
|
||||||
|
|
@ -1238,6 +1238,11 @@ impl RestBackend {
|
||||||
let mut seen_fmris = HashSet::new();
|
let mut seen_fmris = HashSet::new();
|
||||||
|
|
||||||
for part_name in parts.keys() {
|
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) {
|
if let Some(part) = catalog_manager.get_part(part_name) {
|
||||||
// Match stems against pattern
|
// Match stems against pattern
|
||||||
for (publisher_in_catalog, stems) in &part.packages {
|
for (publisher_in_catalog, stems) in &part.packages {
|
||||||
|
|
@ -1248,16 +1253,13 @@ impl RestBackend {
|
||||||
for (stem, versions) in stems {
|
for (stem, versions) in stems {
|
||||||
let matches = if pattern == "*" {
|
let matches = if pattern == "*" {
|
||||||
true
|
true
|
||||||
} else if pattern.contains('*') {
|
} else {
|
||||||
// Basic glob matching (stem matching pattern)
|
let re_str = super::file_backend::glob_to_regex(pattern);
|
||||||
let re_pattern = pattern.replace('*', ".*");
|
if let Ok(re) = regex::Regex::new(&re_str) {
|
||||||
if let Ok(re) = regex::Regex::new(&format!("^{}$", re_pattern)) {
|
|
||||||
re.is_match(stem)
|
re.is_match(stem)
|
||||||
} else {
|
} else {
|
||||||
stem == pattern
|
stem == pattern
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
stem == pattern
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches {
|
if matches {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use libips::repository::{
|
||||||
};
|
};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing::info;
|
use tracing::{info, warn};
|
||||||
use tracing_subscriber::{EnvFilter, fmt};
|
use tracing_subscriber::{EnvFilter, fmt};
|
||||||
|
|
||||||
struct ConsoleProgressReporter;
|
struct ConsoleProgressReporter;
|
||||||
|
|
@ -55,22 +55,15 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
let cli = Cli::parse();
|
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;
|
let progress = ConsoleProgressReporter;
|
||||||
|
|
||||||
// Determine if source is a URL or a path and receive packages
|
// Determine if source is a URL or a path and receive packages
|
||||||
if cli.source.starts_with("http://") || cli.source.starts_with("https://") {
|
if cli.source.starts_with("http://") || cli.source.starts_with("https://") {
|
||||||
let source_repo = RestBackend::open(&cli.source).into_diagnostic()?;
|
let source_repo = RestBackend::open(&cli.source).into_diagnostic()?;
|
||||||
let dest_repo = FileBackend::open(&cli.dest).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);
|
let mut receiver = PackageReceiver::new(&source_repo, dest_repo);
|
||||||
receiver = receiver.with_progress(&progress);
|
receiver = receiver.with_progress(&progress);
|
||||||
receiver
|
receiver
|
||||||
|
|
@ -79,6 +72,9 @@ fn main() -> Result<()> {
|
||||||
} else {
|
} else {
|
||||||
let source_repo = FileBackend::open(&cli.source).into_diagnostic()?;
|
let source_repo = FileBackend::open(&cli.source).into_diagnostic()?;
|
||||||
let dest_repo = FileBackend::open(&cli.dest).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);
|
let mut receiver = PackageReceiver::new(&source_repo, dest_repo);
|
||||||
receiver = receiver.with_progress(&progress);
|
receiver = receiver.with_progress(&progress);
|
||||||
receiver
|
receiver
|
||||||
|
|
@ -90,3 +86,59 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
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