Skip to content

Commit

Permalink
feat(cpp)!: Specify solvable requirements with Requirement
Browse files Browse the repository at this point in the history
  • Loading branch information
eviltak committed Jul 30, 2024
1 parent e3ce7d5 commit 05ac114
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 18 deletions.
21 changes: 20 additions & 1 deletion cpp/include/resolvo.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@
#include "resolvo_internal.h"

namespace resolvo {
using cbindgen_private::Requirement;

/**
* Specifies a requirement (dependency) of a single version set.
*/
inline Requirement requirement_single(VersionSetId id) {
return cbindgen_private::resolvo_requirement_single(id);
}

/**
* Specifies a requirement (dependency) of the union (logical OR) of multiple version sets.
* A solvable belonging to any of the version sets contained in the union satisfies the
* requirement. This variant is typically used for requirements that can be satisfied by two
* or more version sets belonging to different packages.
*/
inline Requirement requirement_union(VersionSetUnionId id) {
return cbindgen_private::resolvo_requirement_union(id);
}

/**
* Called to solve a package problem.
Expand All @@ -12,7 +30,7 @@ namespace resolvo {
* stored in `result`. If the solve was unsuccesfull an error describing the reason is returned and
* the result vector will be empty.
*/
inline String solve(DependencyProvider &provider, Slice<VersionSetId> requirements,
inline String solve(DependencyProvider &provider, Slice<Requirement> requirements,
Slice<VersionSetId> constraints, Vector<SolvableId> &result) {
cbindgen_private::DependencyProvider bridge{
static_cast<void *>(&provider),
Expand All @@ -24,6 +42,7 @@ inline String solve(DependencyProvider &provider, Slice<VersionSetId> requiremen
private_api::bridge_display_string,
private_api::bridge_version_set_name,
private_api::bridge_solvable_name,
private_api::bridge_version_sets_in_union,
private_api::bridge_get_candidates,
private_api::bridge_sort_candidates,
private_api::bridge_filter_candidates,
Expand Down
11 changes: 11 additions & 0 deletions cpp/include/resolvo_dependency_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ using cbindgen_private::NameId;
using cbindgen_private::SolvableId;
using cbindgen_private::StringId;
using cbindgen_private::VersionSetId;
using cbindgen_private::VersionSetUnionId;

/**
* An interface that implements ecosystem specific logic.
Expand Down Expand Up @@ -75,6 +76,11 @@ struct DependencyProvider {
*/
virtual NameId solvable_name(SolvableId solvable_id) = 0;

/**
* Returns the version sets comprising the given union.
*/
virtual Slice<VersionSetId> version_sets_in_union(VersionSetUnionId version_set_union_id) = 0;

/**
* Obtains a list of solvables that should be considered when a package
* with the given name is requested.
Expand Down Expand Up @@ -132,6 +138,11 @@ extern "C" inline NameId bridge_version_set_name(void *data, VersionSetId versio
extern "C" inline NameId bridge_solvable_name(void *data, SolvableId solvable_id) {
return reinterpret_cast<DependencyProvider *>(data)->solvable_name(solvable_id);
}
extern "C" inline Slice<VersionSetId> bridge_version_sets_in_union(
void *data, VersionSetUnionId version_set_union_id) {
return reinterpret_cast<DependencyProvider *>(data)->version_sets_in_union(
version_set_union_id);
}

extern "C" inline void bridge_get_candidates(void *data, NameId package, Candidates *result) {
*result = reinterpret_cast<DependencyProvider *>(data)->get_candidates(package);
Expand Down
95 changes: 93 additions & 2 deletions cpp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,66 @@ impl From<SolvableId> for resolvo::SolvableId {
}
}

/// Specifies the dependency of a solvable on a set of version sets.
/// cbindgen:derive-eq
/// cbindgen:derive-neq
#[repr(C)]
#[derive(Copy, Clone)]
pub enum Requirement {
/// Specifies a dependency on a single version set.
/// cbindgen:derive-eq
/// cbindgen:derive-neq
Single(VersionSetId),
/// Specifies a dependency on the union (logical OR) of multiple version sets. A solvable
/// belonging to ANY of the version sets contained in the union satisfies the requirement.
/// This variant is typically used for requirements that can be satisfied by two or more
/// version sets belonging to different packages.
/// cbindgen:derive-eq
/// cbindgen:derive-neq
Union(VersionSetUnionId),
}

impl From<resolvo::Requirement> for crate::Requirement {
fn from(value: resolvo::Requirement) -> Self {
match value {
resolvo::Requirement::Single(id) => Requirement::Single(id.into()),
resolvo::Requirement::Union(id) => Requirement::Union(id.into()),
}
}
}

impl From<crate::Requirement> for resolvo::Requirement {
fn from(value: crate::Requirement) -> Self {
match value {
Requirement::Single(id) => resolvo::Requirement::Single(id.into()),
Requirement::Union(id) => resolvo::Requirement::Union(id.into()),
}
}
}

/// A unique identifier for a version set union. A version set union describes
/// the union (logical OR) of a non-empty set of version sets belonging to
/// more than one package.
/// cbindgen:derive-eq
/// cbindgen:derive-neq
#[repr(C)]
#[derive(Copy, Clone)]
pub struct VersionSetUnionId {
id: u32,
}

impl From<resolvo::VersionSetUnionId> for crate::VersionSetUnionId {
fn from(id: resolvo::VersionSetUnionId) -> Self {
Self { id: id.0 }
}
}

impl From<crate::VersionSetUnionId> for resolvo::VersionSetUnionId {
fn from(id: crate::VersionSetUnionId) -> Self {
Self(id.id)
}
}

/// A unique identifier for a single version set. A version set describes a
/// set of versions.
/// cbindgen:derive-eq
Expand Down Expand Up @@ -102,7 +162,7 @@ pub struct Dependencies {
/// A pointer to the first element of a list of requirements. Requirements
/// defines which packages should be installed alongside the depending
/// package and the constraints applied to the package.
pub requirements: Vector<VersionSetId>,
pub requirements: Vector<Requirement>,

/// Defines additional constraints on packages that may or may not be part
/// of the solution. Different from `requirements`, packages in this set
Expand Down Expand Up @@ -230,6 +290,12 @@ pub struct DependencyProvider {
/// Returns the name of the package for the given solvable.
pub solvable_name: unsafe extern "C" fn(data: *mut c_void, solvable_id: SolvableId) -> NameId,

/// Returns the version sets comprising the given union.
pub version_sets_in_union: unsafe extern "C" fn(
data: *mut c_void,
version_set_union_id: VersionSetUnionId,
) -> Slice<'static, VersionSetId>,

/// Obtains a list of solvables that should be considered when a package
/// with the given name is requested.
pub get_candidates:
Expand Down Expand Up @@ -314,6 +380,17 @@ impl<'d> resolvo::Interner for &'d DependencyProvider {
fn solvable_name(&self, solvable: resolvo::SolvableId) -> resolvo::NameId {
unsafe { (self.solvable_name)(self.data, solvable.into()) }.into()
}

fn version_sets_in_union(
&self,
version_set_union: resolvo::VersionSetUnionId,
) -> impl Iterator<Item = resolvo::VersionSetId> {
unsafe { (self.version_sets_in_union)(self.data, version_set_union.into()) }
.as_slice()
.into_iter()
.copied()
.map(Into::into)
}
}

impl<'d> resolvo::DependencyProvider for &'d DependencyProvider {
Expand Down Expand Up @@ -400,7 +477,7 @@ impl<'d> resolvo::DependencyProvider for &'d DependencyProvider {
#[allow(unused)]
pub extern "C" fn resolvo_solve(
provider: &DependencyProvider,
requirements: Slice<VersionSetId>,
requirements: Slice<Requirement>,
constraints: Slice<VersionSetId>,
error: &mut String,
result: &mut Vector<SolvableId>,
Expand Down Expand Up @@ -433,6 +510,20 @@ pub extern "C" fn resolvo_solve(
}
}

#[no_mangle]
#[allow(unused)]
pub extern "C" fn resolvo_requirement_single(version_set_id: VersionSetId) -> Requirement {
Requirement::Single(version_set_id)
}

#[no_mangle]
#[allow(unused)]
pub extern "C" fn resolvo_requirement_union(
version_set_union_id: VersionSetUnionId,
) -> Requirement {
Requirement::Union(version_set_union_id)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
107 changes: 92 additions & 15 deletions cpp/tests/solve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ struct Candidate {
};

/**
* A requirement for a package.
* A version set for a package.
*/
struct Requirement {
struct VersionSet {
resolvo::NameId name;
uint32_t version_start;
uint32_t version_end;
Expand All @@ -31,19 +31,47 @@ struct PackageDatabase : public resolvo::DependencyProvider {
resolvo::Pool<resolvo::NameId, resolvo::String> names;
resolvo::Pool<resolvo::StringId, resolvo::String> strings;
std::vector<Candidate> candidates;
std::vector<Requirement> requirements;
std::vector<VersionSet> version_sets;
std::vector<std::vector<resolvo::VersionSetId>> version_set_unions;

/**
* Allocates a new requirement and return the id of the requirement.
* Allocates a new version set and return the id of the version set.
*/
resolvo::VersionSetId alloc_requirement(std::string_view package, uint32_t version_start,
resolvo::VersionSetId alloc_version_set(std::string_view package, uint32_t version_start,
uint32_t version_end) {
auto name_id = names.alloc(std::move(package));
auto id = resolvo::VersionSetId{static_cast<uint32_t>(requirements.size())};
requirements.push_back(Requirement{name_id, version_start, version_end});
auto id = resolvo::VersionSetId{static_cast<uint32_t>(version_sets.size())};
version_sets.push_back(VersionSet{name_id, version_start, version_end});
return id;
}

/**
* Allocates a new requirement for a single version set.
*/
resolvo::Requirement alloc_requirement(std::string_view package, uint32_t version_start,
uint32_t version_end) {
auto id = alloc_version_set(package, version_start, version_end);
return resolvo::requirement_single(id);
}

/**
* Allocates a new requirement for a version set union.
*/
resolvo::Requirement alloc_requirement_union(
std::initializer_list<std::tuple<std::string_view, uint32_t, uint32_t>> version_sets) {
std::vector<resolvo::VersionSetId> version_set_union{version_sets.size()};

auto version_sets_it = version_sets.begin();
for (size_t i = 0; i < version_sets.size(); ++i, ++version_sets_it) {
auto [package, version_start, version_end] = *version_sets_it;
version_set_union[i] = alloc_version_set(package, version_start, version_end);
}

auto id = resolvo::VersionSetUnionId{static_cast<uint32_t>(version_set_unions.size())};
version_set_unions.push_back(std::move(version_set_union));
return resolvo::requirement_union(id);
}

/**
* Allocates a new candidate and return the id of the candidate.
*/
Expand Down Expand Up @@ -90,7 +118,7 @@ struct PackageDatabase : public resolvo::DependencyProvider {
}

resolvo::String display_version_set(resolvo::VersionSetId version_set) override {
const auto& req = requirements[version_set.id];
const auto& req = version_sets[version_set.id];
std::stringstream ss;
ss << req.version_start << ".." << req.version_end;
return resolvo::String(ss.str());
Expand All @@ -101,13 +129,19 @@ struct PackageDatabase : public resolvo::DependencyProvider {
}

resolvo::NameId version_set_name(resolvo::VersionSetId version_set_id) override {
return requirements[version_set_id.id].name;
return version_sets[version_set_id.id].name;
}

resolvo::NameId solvable_name(resolvo::SolvableId solvable_id) override {
return candidates[solvable_id.id].name;
}

resolvo::Slice<resolvo::VersionSetId> version_sets_in_union(
resolvo::VersionSetUnionId version_set_union_id) override {
const auto& version_set_ids = version_set_unions[version_set_union_id.id];
return {version_set_ids.data(), version_set_ids.size()};
}

resolvo::Candidates get_candidates(resolvo::NameId package) override {
resolvo::Candidates result;

Expand Down Expand Up @@ -137,11 +171,11 @@ struct PackageDatabase : public resolvo::DependencyProvider {
resolvo::Slice<resolvo::SolvableId> solvables, resolvo::VersionSetId version_set_id,
bool inverse) override {
resolvo::Vector<resolvo::SolvableId> result;
const auto& requirement = requirements[version_set_id.id];
const auto& version_set = version_sets[version_set_id.id];
for (auto solvable : solvables) {
const auto& candidate = candidates[solvable.id];
bool matches = candidate.version >= requirement.version_start &&
candidate.version < requirement.version_end;
bool matches = candidate.version >= version_set.version_start &&
candidate.version < version_set.version_end;
if (matches != inverse) {
result.push_back(solvable);
}
Expand Down Expand Up @@ -183,9 +217,9 @@ SCENARIO("Solve") {
auto c_1 = db.alloc_candidate("c", 1, {});

// Construct a problem to be solved by the solver
resolvo::Vector<resolvo::VersionSetId> requirements = {db.alloc_requirement("a", 1, 3)};
resolvo::Vector<resolvo::VersionSetId> constraints = {db.alloc_requirement("b", 1, 3),
db.alloc_requirement("c", 1, 3)};
resolvo::Vector<resolvo::Requirement> requirements = {db.alloc_requirement("a", 1, 3)};
resolvo::Vector<resolvo::VersionSetId> constraints = {db.alloc_version_set("b", 1, 3),
db.alloc_version_set("c", 1, 3)};

// Solve the problem
resolvo::Vector<resolvo::SolvableId> result;
Expand All @@ -196,3 +230,46 @@ SCENARIO("Solve") {
REQUIRE(result[0] == a_2);
REQUIRE(result[1] == b_2);
}

SCENARIO("Solve Union") {
/// Construct a database with packages a, b, and c.
PackageDatabase db;

// Check that PackageDatabase correctly implements the DependencyProvider interface
static_assert(std::has_virtual_destructor_v<PackageDatabase>);
static_assert(std::is_polymorphic_v<PackageDatabase>);
static_assert(std::is_base_of_v<resolvo::DependencyProvider, PackageDatabase>);

auto a_1 = db.alloc_candidate("a", 1, {});

auto b_1 = db.alloc_candidate("b", 1, {});

auto c_1 = db.alloc_candidate("c", 1, {{db.alloc_requirement("a", 1, 10)}, {}});

auto d_1 = db.alloc_candidate("d", 1, {{db.alloc_requirement("b", 1, 10)}, {}});

auto e_1 = db.alloc_candidate("e", 1,
{{db.alloc_requirement_union({{"a", 1, 10}, {"b", 1, 10}})}, {}});

auto f_1 = db.alloc_candidate(
"f", 1, {{db.alloc_requirement("b", 1, 10)}, {db.alloc_version_set("a", 10, 20)}});

// Construct a problem to be solved by the solver
resolvo::Vector<resolvo::Requirement> requirements = {
db.alloc_requirement_union({{"c", 1, 10}, {"d", 1, 10}}),
db.alloc_requirement("e", 1, 10),
db.alloc_requirement("f", 1, 10),
};
resolvo::Vector<resolvo::VersionSetId> constraints = {};

// Solve the problem
resolvo::Vector<resolvo::SolvableId> result;
resolvo::solve(db, requirements, constraints, result);

// Check the result
REQUIRE(result.size() == 4);
REQUIRE(result[0] == f_1);
REQUIRE(result[1] == e_1);
REQUIRE(result[2] == b_1);
REQUIRE(result[3] == d_1);
}

0 comments on commit 05ac114

Please sign in to comment.