use super::{super::c_string, ffi, pool::Pool, repodata::Repodata, solvable::SolvableId};
use std::{marker::PhantomData, ptr::NonNull};
pub struct Repo<'pool>(NonNull<ffi::Repo>, PhantomData<&'pool ffi::Repo>);
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct RepoId(usize);
impl RepoId {
pub fn from_ffi_solvable(solvable: &ffi::Solvable) -> RepoId {
RepoId(solvable.repo as usize)
}
}
impl Drop for Repo<'_> {
fn drop(&mut self) {
unsafe { ffi::repo_free(self.0.as_mut(), 1) }
}
}
impl Repo<'_> {
pub fn new(pool: &Pool, url: impl AsRef<str>, priority: i32) -> Repo<'_> {
let c_url = c_string(url);
unsafe {
let repo_ptr = ffi::repo_create(pool.raw_ptr(), c_url.as_ptr());
let mut non_null_ptr = NonNull::new(repo_ptr).expect("repo ptr was null");
non_null_ptr.as_mut().priority = priority;
Repo(non_null_ptr, PhantomData)
}
}
pub fn ensure_belongs_to_pool(&self, pool: &Pool) {
let repo_pool_ptr = unsafe { self.0.as_ref().pool };
assert_eq!(
repo_pool_ptr,
pool.raw_ptr(),
"repo does not belong to the provided pool"
);
}
pub fn id(&self) -> RepoId {
RepoId(self.raw_ptr() as usize)
}
pub fn raw_ptr(&self) -> *mut ffi::Repo {
self.0.as_ptr()
}
pub fn add_repodata(&self) -> Repodata<'_> {
unsafe {
let repodata_ptr = ffi::repo_add_repodata(self.raw_ptr(), 0);
Repodata::from_ptr(self, repodata_ptr)
}
}
pub fn add_solv(&self, pool: &Pool, file: *mut libc::FILE) {
let result = unsafe { ffi::repo_add_solv(self.raw_ptr(), file.cast(), 0) };
assert_eq!(result, 0, "add_solv failed: {}", pool.last_error());
}
pub fn add_solvable(&self) -> SolvableId {
SolvableId(unsafe { ffi::repo_add_solvable(self.raw_ptr()) })
}
pub fn add_requires(&self, solvable: &mut ffi::Solvable, rel_id: ffi::Id) {
solvable.requires =
unsafe { ffi::repo_addid_dep(self.raw_ptr(), solvable.requires, rel_id, 0) };
}
pub fn add_provides(&self, solvable: &mut ffi::Solvable, rel_id: ffi::Id) {
solvable.provides =
unsafe { ffi::repo_addid_dep(self.raw_ptr(), solvable.provides, rel_id, 0) };
}
pub fn write(&self, pool: &Pool, file: *mut libc::FILE) {
let result = unsafe { ffi::repo_write(self.raw_ptr(), file.cast()) };
assert_eq!(result, 0, "repo_write failed: {}", pool.last_error());
}
pub fn internalize(&self) {
unsafe { ffi::repo_internalize(self.raw_ptr()) }
}
pub unsafe fn free_solvable(&self, solvable_id: SolvableId) {
ffi::repo_free_solvable(self.raw_ptr(), solvable_id.into(), 1);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::libsolv_c::wrapper::pool::StringId;
#[test]
fn test_repo_creation() {
let pool = Pool::default();
let mut _repo = Repo::new(&pool, "conda-forge", 0);
}
#[test]
fn test_repo_solv_roundtrip() {
let dir = tempfile::tempdir().unwrap();
let solv_path = dir.path().join("repo.solv").to_string_lossy().into_owned();
let solv_path = c_string(solv_path);
{
let pool = Pool::default();
let repo = Repo::new(&pool, "conda-forge", 0);
let solvable_id = repo.add_solvable();
let solvable = unsafe { solvable_id.resolve_raw(&pool).as_mut() };
solvable.name = pool.intern_str("dummy-solvable").into();
let mode = c_string("wb");
let file = unsafe { libc::fopen(solv_path.as_ptr(), mode.as_ptr()) };
repo.write(&pool, file);
unsafe { libc::fclose(file) };
}
let pool = Pool::default();
let repo = Repo::new(&pool, "conda-forge", 0);
let mode = c_string("rb");
let file = unsafe { libc::fopen(solv_path.as_ptr(), mode.as_ptr()) };
repo.add_solv(&pool, file);
unsafe { libc::fclose(file) };
let repo = unsafe { repo.0.as_ref() };
assert_eq!(repo.nsolvables, 1);
let ffi_pool = pool.as_ref();
let solvable = unsafe { *ffi_pool.solvables.offset(2) };
let name = StringId(solvable.name).resolve(&pool).unwrap();
assert_eq!(name, "dummy-solvable");
}
}