diff --git a/rust/zypp-agama/fixtures/zypp_services_root/service/repo/repoindex.xml b/rust/zypp-agama/fixtures/zypp_services_root/service/repo/repoindex.xml new file mode 100644 index 0000000000..b5251e05b1 --- /dev/null +++ b/rust/zypp-agama/fixtures/zypp_services_root/service/repo/repoindex.xml @@ -0,0 +1,4 @@ + + + + diff --git a/rust/zypp-agama/src/lib.rs b/rust/zypp-agama/src/lib.rs index 510049550a..dbd9297116 100644 --- a/rust/zypp-agama/src/lib.rs +++ b/rust/zypp-agama/src/lib.rs @@ -474,6 +474,29 @@ impl Zypp { } } + pub fn add_service(&self, alias: &str, url: &str) -> ZyppResult<()> { + unsafe { + let mut status: Status = Status::default(); + let status_ptr = &mut status as *mut _; + let c_alias = CString::new(alias).unwrap(); + let c_url = CString::new(url).unwrap(); + zypp_agama_sys::add_service(self.ptr, c_alias.as_ptr(), c_url.as_ptr(), status_ptr); + + helpers::status_to_result_void(status) + } + } + + pub fn refresh_service(&self, alias: &str) -> ZyppResult<()> { + unsafe { + let mut status: Status = Status::default(); + let status_ptr = &mut status as *mut _; + let c_alias = CString::new(alias).unwrap(); + zypp_agama_sys::refresh_service(self.ptr, c_alias.as_ptr(), status_ptr); + + helpers::status_to_result_void(status) + } + } + pub fn create_repo_cache(&self, alias: &str, progress: F) -> ZyppResult<()> where F: FnMut(i64, String) -> bool, diff --git a/rust/zypp-agama/tests/services.rs b/rust/zypp-agama/tests/services.rs new file mode 100644 index 0000000000..c87cfbbb01 --- /dev/null +++ b/rust/zypp-agama/tests/services.rs @@ -0,0 +1,63 @@ +// Copyright (c) [2025] SUSE LLC +// +// All Rights Reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, contact SUSE LLC. +// +// To contact SUSE LLC about this file by physical or electronic mail, you may +// find current contact information at www.suse.com. + +use std::{fs, io, path::Path}; + +fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { + fs::create_dir_all(&dst)?; + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; + } else { + fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} + +#[test] +fn test_services() { + let root_dir = env!("CARGO_MANIFEST_DIR").to_string() + "/fixtures/zypp_services_root"; + + let zypp_root = env!("CARGO_MANIFEST_DIR").to_string() + "/fixtures/zypp_root_tmp"; + if fs::exists(&zypp_root).unwrap() { + fs::remove_dir_all(&zypp_root).unwrap(); + } + fs::create_dir_all(&zypp_root).unwrap(); + + copy_dir_all(Path::new(&root_dir), Path::new(&zypp_root)).unwrap(); + + let zypp = zypp_agama::Zypp::init_target(&zypp_root, |_, _, _| {}).unwrap(); + + let service_url = "file:///service"; + println!("{}", service_url); + + zypp.add_service("test", service_url).unwrap(); + zypp.refresh_service("test").unwrap(); + + let repos = zypp.list_repositories().unwrap(); + assert!( + repos.len() == 2, + "Unexpected repos count. Repos: {:#?}", + repos + ); +} diff --git a/rust/zypp-agama/zypp-agama-sys/c-layer/include/repository.h b/rust/zypp-agama/zypp-agama-sys/c-layer/include/repository.h index a00dce78be..b2018d3565 100644 --- a/rust/zypp-agama/zypp-agama-sys/c-layer/include/repository.h +++ b/rust/zypp-agama/zypp-agama-sys/c-layer/include/repository.h @@ -65,6 +65,14 @@ void remove_repository(struct Zypp *zypp, const char *alias, struct Status *status, ZyppProgressCallback callback, void *user_data) noexcept; +/// Adds service to repo manager +/// @param zypp see \ref init_target +/// @param alias have to be unique +/// @param url +/// @param[out] status (will overwrite existing contents) +void add_service(struct Zypp *zypp, const char *alias, const char *url, + struct Status *status) noexcept; + /// /// @param zypp see \ref init_target /// @param alias alias of repository to refresh @@ -77,6 +85,13 @@ void refresh_repository(struct Zypp *zypp, const char *alias, struct DownloadProgressCallbacks *progress, struct SecurityCallbacks *security) noexcept; +/// +/// @param zypp see \ref init_target +/// @param alias alias of service to refresh +/// @param[out] status (will overwrite existing contents) +void refresh_service(struct Zypp *zypp, const char *alias, + struct Status *status) noexcept; + void build_repository_cache(struct Zypp *zypp, const char *alias, struct Status *status, ZyppProgressCallback callback, diff --git a/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx b/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx index 2e177fa724..d194fc7f9e 100644 --- a/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx +++ b/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx @@ -150,7 +150,8 @@ void switch_target(struct Zypp *zypp, const char *root, false /* rebuild rpmdb: no */); // switch cache for repositories, otherwise we run out of space in tmpfs - // see https://github.com/yast/yast-pkg-bindings/blob/853496f527543e6d51730fd7e3126ad94b13c303/src/PkgFunctions.cc#L496 + // see + // https://github.com/yast/yast-pkg-bindings/blob/853496f527543e6d51730fd7e3126ad94b13c303/src/PkgFunctions.cc#L496 zypp::RepoManagerOptions repo_options(root); zypp::Pathname packages_prefix = repo_options.repoPackagesCachePath; @@ -424,6 +425,44 @@ bool run_solver(struct Zypp *zypp, struct Status *status) noexcept { } } +void add_service(struct Zypp *zypp, const char *alias, const char *url, + struct Status *status) noexcept { + if (zypp->repo_manager == NULL) { + STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); + return; + } + try { + zypp::ServiceInfo zypp_service = zypp::ServiceInfo(alias); + zypp_service.setUrl(zypp::Url(url)); + + zypp->repo_manager->addService(zypp_service); + STATUS_OK(status); + } catch (zypp::Exception &excpt) { + STATUS_EXCEPT(status, excpt); + } +} + +void refresh_service(struct Zypp *zypp, const char *alias, + struct Status *status) noexcept { + if (zypp->repo_manager == NULL) { + STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); + return; + } + try { + zypp::ServiceInfo service = zypp->repo_manager->getService(alias); + if (service == zypp::ServiceInfo::noService) { + STATUS_ERROR(status, + "Cannot refresh service with alias %s. Service not found.", + alias); + return; + } + zypp->repo_manager->refreshService(service); + STATUS_OK(status); + } catch (zypp::Exception &excpt) { + STATUS_EXCEPT(status, excpt); + } +} + void refresh_repository(struct Zypp *zypp, const char *alias, struct Status *status, struct DownloadProgressCallbacks *callbacks, diff --git a/rust/zypp-agama/zypp-agama-sys/src/bindings.rs b/rust/zypp-agama/zypp-agama-sys/src/bindings.rs index 7dd95d4855..8191764010 100644 --- a/rust/zypp-agama/zypp-agama-sys/src/bindings.rs +++ b/rust/zypp-agama/zypp-agama-sys/src/bindings.rs @@ -683,6 +683,13 @@ unsafe extern "C" { callback: ZyppProgressCallback, user_data: *mut ::std::os::raw::c_void, ); + #[doc = " Adds service to repo manager\n @param zypp see \\ref init_target\n @param alias have to be unique\n @param url\n @param[out] status (will overwrite existing contents)"] + pub fn add_service( + zypp: *mut Zypp, + alias: *const ::std::os::raw::c_char, + url: *const ::std::os::raw::c_char, + status: *mut Status, + ); #[doc = "\n @param zypp see \\ref init_target\n @param alias alias of repository to refresh\n @param[out] status (will overwrite existing contents)\n @param progress pointer to struct with callbacks or NULL if no progress is\n needed\n @param security pointer to struct with security callbacks"] pub fn refresh_repository( zypp: *mut Zypp, @@ -691,6 +698,12 @@ unsafe extern "C" { progress: *mut DownloadProgressCallbacks, security: *mut SecurityCallbacks, ); + #[doc = "\n @param zypp see \\ref init_target\n @param alias alias of service to refresh\n @param[out] status (will overwrite existing contents)"] + pub fn refresh_service( + zypp: *mut Zypp, + alias: *const ::std::os::raw::c_char, + status: *mut Status, + ); pub fn build_repository_cache( zypp: *mut Zypp, alias: *const ::std::os::raw::c_char,