pubsat 0.1.0

Building blocks for SAT-based dependency resolvers: a node-semver-compatible range parser, an ecosystem-independent constraint vocabulary, and a backend-agnostic SAT problem/solver abstraction with a Varisat backend.
Documentation
//! End-to-end resolution against an in-memory `MockRegistry`.
//!
//! Demonstrates the full pipeline: register packages and their
//! dependencies, build a dependency graph, hand it to the
//! resolver, and inspect the selected versions.
//!
//! Run with:
//!
//! ```text
//! cargo run --example resolve_mock
//! ```

use std::sync::Arc;

use pubsat::builder::DependencyGraphBuilder;
use pubsat::registry::{MockRegistry, VersionMetadata};
use pubsat::resolver::DependencyResolver;
use pubsat::version::VersionSet;
use semver::Version;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Stand up a registry.
    //
    // The mock registry supports a builder-style API. Real
    // implementations would wrap an HTTP client or read from an
    // on-disk index.
    let registry = Arc::new(
        MockRegistry::new()
            .with_versions("left-pad", &["1.0.0", "1.1.0", "1.3.0"])
            .with_versions("chalk", &["4.1.0", "4.1.2", "5.0.0"])
            .with_versions("ansi-styles", &["4.3.0", "5.2.0", "6.2.1"])
            // chalk 4.x depends on ansi-styles ^4
            .with_dependency("chalk", "4.1.0", "ansi-styles", parse_set("^4.0.0"))
            .with_dependency("chalk", "4.1.2", "ansi-styles", parse_set("^4.0.0"))
            // chalk 5.x depends on ansi-styles ^6
            .with_dependency("chalk", "5.0.0", "ansi-styles", parse_set("^6.0.0")),
    );

    // 2. Describe what we want at the top level — a synthetic root
    //    "my-app" that depends on chalk ^5 and left-pad ^1.
    let root = VersionMetadata::new("my-app", Version::new(1, 0, 0))
        .with_dependency("chalk", parse_set("^5.0.0"))
        .with_dependency("left-pad", parse_set("^1.0.0"));

    // 3. Walk the registry to build a dependency graph.
    let mut builder = DependencyGraphBuilder::new(registry.clone());
    let graph = builder.build(root).await?;
    println!(
        "Built dependency graph: {} packages, {} edges (in {} ms)",
        graph.package_count(),
        graph.dependency_count(),
        builder.stats().build_duration_ms,
    );

    // 4. Resolve the graph to a concrete version selection.
    let resolver = DependencyResolver::new(registry);
    let resolution = resolver.resolve(graph).await?;

    println!(
        "\nResolution: {} packages, {} SAT variables, {} clauses, solved in {:?}",
        resolution.packages.len(),
        resolution.variables_count,
        resolution.clauses_count,
        resolution.resolution_time,
    );
    for pkg in &resolution.packages {
        println!(
            "  {} {}  ({:?}, from {})",
            pkg.name, pkg.version, pkg.dependency_type, pkg.resolved_from,
        );
    }

    Ok(())
}

fn parse_set(s: &str) -> VersionSet {
    s.parse().expect("valid version-set syntax")
}