pub struct VersionResolver { /* private fields */ }
Expand description
Centralized version resolver for batch SHA resolution.
The VersionResolver
manages the collection and resolution of all dependency
versions in a single batch operation, enabling optimal Git repository access
patterns and maximum worktree reuse.
Implementations§
Source§impl VersionResolver
impl VersionResolver
Sourcepub fn add_version(&mut self, source: &str, url: &str, version: Option<&str>)
pub fn add_version(&mut self, source: &str, url: &str, version: Option<&str>)
Adds a version to be resolved
Multiple calls with the same (source, version) pair will be deduplicated.
§Arguments
source
- Source name from manifesturl
- Git repository URLversion
- Version specification (tag, branch, commit, or None for HEAD)
Sourcepub async fn resolve_all(&mut self) -> Result<()>
pub async fn resolve_all(&mut self) -> Result<()>
Resolves all collected versions to their commit SHAs using cached repositories.
This method implements the second phase of AGPM’s two-phase resolution architecture.
It processes all version entries collected via add_version()
calls and resolves
them to concrete commit SHAs using locally cached Git repositories.
§Prerequisites
CRITICAL: pre_sync_sources()
must be called before this method. The resolver
requires all repositories to be pre-synced to the cache, and will return an error
if any required repository is missing from the bare_repos
map.
§Resolution Process
The method performs the following steps:
- Source Grouping: Groups entries by source to minimize repository operations
- Repository Access: Uses pre-synced repositories from
pre_sync_sources()
- Version Constraint Resolution: Handles semver constraints (
^1.0
,~2.1
) - SHA Resolution: Resolves all versions to SHAs using
git rev-parse
- Result Caching: Stores resolved SHAs for quick retrieval
§Version Resolution Strategy
The resolver handles different version types:
- Exact SHAs: Used directly without resolution
- Semantic Versions: Resolved using semver constraint matching
- Tags: Resolved to their commit SHAs
- Branch Names: Resolved to current HEAD commit
- Latest/None: Defaults to the repository’s default branch
§Performance Characteristics
- Time Complexity: O(n·log(t)) where n = entries, t = tags per repo
- Space Complexity: O(n) for storing resolved results
- Network I/O: Zero (operates on cached repositories only)
- Parallelization: Single-threaded but optimized for batch operations
§Example
let cache = Cache::new()?;
let mut resolver = VersionResolver::new(cache);
// Add various version types
resolver.add_version("source", "https://github.com/org/repo.git", Some("v1.2.3"));
resolver.add_version("source", "https://github.com/org/repo.git", Some("^1.0"));
resolver.add_version("source", "https://github.com/org/repo.git", Some("main"));
resolver.add_version("source", "https://github.com/org/repo.git", None); // latest
// Phase 1: Sync repositories
resolver.pre_sync_sources().await?;
// Phase 2: Resolve versions to SHAs (this method)
resolver.resolve_all().await?;
// Access resolved SHAs
if resolver.is_resolved("source", "v1.2.3") {
println!("v1.2.3 resolved successfully");
}
§Error Handling
The method uses fail-fast behavior - if any version resolution fails, the entire operation is aborted. This ensures consistency and prevents partial resolution states.
§Errors
Returns an error if:
- Pre-sync Required: Repository was not pre-synced (call
pre_sync_sources()
first) - Version Not Found: Specified version/tag/branch doesn’t exist in repository
- Constraint Resolution: Semver constraint cannot be satisfied by available tags
- Git Operations:
git rev-parse
or other Git commands fail - Repository Access: Cached repository is corrupted or inaccessible
Sourcepub async fn resolve_single(
&mut self,
source: &str,
url: &str,
version: Option<&str>,
) -> Result<String>
pub async fn resolve_single( &mut self, source: &str, url: &str, version: Option<&str>, ) -> Result<String>
Resolves a single version to SHA without affecting the batch
This is useful for incremental resolution or testing.
Sourcepub fn get_resolved_sha(&self, source: &str, version: &str) -> Option<String>
pub fn get_resolved_sha(&self, source: &str, version: &str) -> Option<String>
Gets the resolved SHA for a given source and version
Returns None if the version hasn’t been resolved yet.
§Arguments
source
- Source nameversion
- Version specification (use “HEAD” for None)
Sourcepub fn get_all_resolved(&self) -> HashMap<(String, String), String>
pub fn get_all_resolved(&self) -> HashMap<(String, String), String>
Gets all resolved SHAs as a HashMap
Useful for bulk operations or debugging.
Sourcepub const fn get_all_resolved_full(
&self,
) -> &HashMap<(String, String), ResolvedVersion>
pub const fn get_all_resolved_full( &self, ) -> &HashMap<(String, String), ResolvedVersion>
Gets all resolved versions with both SHA and resolved reference
Returns a HashMap
with (source, version) -> ResolvedVersion
Sourcepub fn is_resolved(&self, source: &str, version: &str) -> bool
pub fn is_resolved(&self, source: &str, version: &str) -> bool
Checks if a specific version has been resolved
Sourcepub async fn pre_sync_sources(&mut self) -> Result<()>
pub async fn pre_sync_sources(&mut self) -> Result<()>
Pre-syncs all unique sources to ensure repositories are cloned/fetched.
This method implements the first phase of AGPM’s two-phase resolution architecture. It is designed to be called during the “Syncing sources” phase to perform all Git network operations upfront, before version resolution occurs.
The method processes all entries in the resolver, groups them by unique source URLs,
and ensures each repository is cloned to the cache with the latest refs fetched.
This enables the subsequent resolve_all()
method to work purely with local
cached data, providing better performance and progress reporting.
§Post-Execution State
After this method completes successfully:
- All required repositories will be cloned to
~/.agpm/cache/sources/
- All repositories will have their latest refs fetched from remote
- The internal
bare_repos
map will be populated with repository paths resolve_all()
can proceed without any network operations
This separation provides several benefits:
- Clear progress phases: Network operations vs. local resolution
- Better error handling: Network failures separated from resolution logic
- Batch optimization: Single clone/fetch per unique repository
- Parallelization potential: Multiple repositories can be synced concurrently
§Example
use agpm_cli::resolver::version_resolver::VersionResolver;
use agpm_cli::cache::Cache;
let cache = Cache::new()?;
let mut version_resolver = VersionResolver::new(cache);
// Add versions to resolve across multiple sources
version_resolver.add_version(
"community",
"https://github.com/org/agpm-community.git",
Some("v1.0.0"),
);
version_resolver.add_version(
"community",
"https://github.com/org/agpm-community.git",
Some("v2.0.0"),
);
version_resolver.add_version(
"private-tools",
"https://github.com/company/private-agpm.git",
Some("main"),
);
// Phase 1: Pre-sync all repositories (network operations)
version_resolver.pre_sync_sources().await?;
// Phase 2: Resolve all versions to SHAs (local operations only)
version_resolver.resolve_all().await?;
// Access resolved data
if version_resolver.is_resolved("community", "v1.0.0") {
println!("Successfully resolved community v1.0.0");
}
§Deduplication
The method automatically deduplicates by source URL - if multiple entries reference the same repository, only one clone/fetch operation is performed. This is particularly efficient when resolving multiple versions from the same source.
§Errors
Returns an error if:
- Repository cloning fails (network issues, authentication, invalid URL)
- Fetching latest refs fails (network connectivity, permission issues)
- Authentication fails for private repositories
- Disk space is insufficient for cloning repositories
- Repository is corrupted and cannot be accessed
Sourcepub fn get_bare_repo_path(&self, source: &str) -> Option<&PathBuf>
pub fn get_bare_repo_path(&self, source: &str) -> Option<&PathBuf>
Gets the bare repository path for a source
Returns None if the source hasn’t been processed yet.
Sourcepub fn clear(&mut self)
pub fn clear(&mut self)
Clears all resolved versions and cached data
Useful for testing or when starting a fresh resolution.
Sourcepub fn pending_count(&self) -> usize
pub fn pending_count(&self) -> usize
Returns the number of unique versions to resolve
Sourcepub fn has_entries(&self) -> bool
pub fn has_entries(&self) -> bool
Checks if the resolver has any entries to resolve.
This is a convenience method to determine if the resolver has been populated
with version entries via add_version()
calls. It’s useful for conditional
logic to avoid unnecessary operations when no versions need resolution.
§Returns
Returns true
if there are entries that need resolution, false
if the
resolver is empty.
§Example
let mut resolver = VersionResolver::new(cache);
assert!(!resolver.has_entries()); // Initially empty
resolver.add_version("source", "https://github.com/org/repo.git", Some("v1.0.0"));
assert!(resolver.has_entries()); // Now has entries
Sourcepub fn resolved_count(&self) -> usize
pub fn resolved_count(&self) -> usize
Returns the number of successfully resolved versions