VersionResolver

Struct VersionResolver 

Source
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

Source

pub fn new(cache: Cache) -> Self

Creates a new version resolver with the given cache

Source

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 manifest
  • url - Git repository URL
  • version - Version specification (tag, branch, commit, or None for HEAD)
Source

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:

  1. Source Grouping: Groups entries by source to minimize repository operations
  2. Repository Access: Uses pre-synced repositories from pre_sync_sources()
  3. Version Constraint Resolution: Handles semver constraints (^1.0, ~2.1)
  4. SHA Resolution: Resolves all versions to SHAs using git rev-parse
  5. 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
Source

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.

Source

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 name
  • version - Version specification (use “HEAD” for None)
Source

pub fn get_all_resolved(&self) -> HashMap<(String, String), String>

Gets all resolved SHAs as a HashMap

Useful for bulk operations or debugging.

Source

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

Source

pub fn is_resolved(&self, source: &str, version: &str) -> bool

Checks if a specific version has been resolved

Source

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
Source

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.

Source

pub fn clear(&mut self)

Clears all resolved versions and cached data

Useful for testing or when starting a fresh resolution.

Source

pub fn pending_count(&self) -> usize

Returns the number of unique versions to resolve

Source

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
Source

pub fn resolved_count(&self) -> usize

Returns the number of successfully resolved versions

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,