use std::{
borrow::Cow,
fmt::Debug,
path::{Path, PathBuf},
};
use ahash::HashMap;
use apt_auth_config::{AuthConfig, Authenticator};
use fancy_regex::Regex;
use futures::StreamExt;
use oma_apt_sources_lists::{
Signature, SourceEntry, SourceLine, SourceListType, SourcesList, SourcesListError,
};
use oma_fetch::{
SingleDownloadError, build_request_with_basic_auth,
reqwest::{Client, Method, Response, StatusCode},
send_request,
};
use oma_utils::concat_url;
use once_cell::sync::OnceCell;
use spdlog::debug;
use tokio::{
fs::{self, File},
io::AsyncWriteExt,
};
use url::Url;
use crate::{
db::{Event, RefreshError, content_length},
util::{DatabaseFilenameReplacer, concat_url_only_check_once_slash},
};
#[derive(Clone)]
pub struct OmaSourceEntry<'a> {
source: SourceEntry,
arch: &'a str,
url: OnceCell<String>,
suite: OnceCell<String>,
dist_path: OnceCell<String>,
from: OnceCell<OmaSourceEntryFrom>,
}
impl Debug for OmaSourceEntry<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OmaSourceEntry")
.field("url", &self.url)
.field("options", &self.source.options)
.field("suite", &self.suite)
.field("dist_path", &self.dist_path)
.field("from", &self.from)
.field("is-src", &self.source.source)
.finish()
}
}
pub(crate) async fn scan_sources_lists_paths(
list_file: impl AsRef<str>,
list_dir: impl AsRef<str>,
) -> Result<Vec<PathBuf>, SourcesListError> {
let mut paths = vec![];
let default = Path::new(list_file.as_ref());
let list_dir_path = Path::new(list_dir.as_ref());
if default.exists() {
paths.push(default.to_path_buf());
}
if list_dir_path.exists() {
let mut dir = tokio::fs::read_dir(list_dir_path).await?;
while let Some(entry) = dir.next_entry().await? {
let path = entry.path();
if !path.is_file() {
continue;
}
paths.push(path.to_path_buf());
}
}
Ok(paths)
}
#[cfg(feature = "apt")]
pub fn ignores(config: &oma_apt::config::Config) -> Vec<Regex> {
use spdlog::warn;
config.find_vector("Dir::Ignore-Files-Silently")
.iter()
.filter_map(|re| Regex::new(re)
.inspect_err(|e|
warn!("Failed to parse regex {} in ignore rule list (Dir::Ignore-Files-Silently): {}", re, e)).ok())
.collect::<Vec<_>>()
}
pub async fn scan_sources_list_from_paths<'a>(
paths: &[impl AsRef<Path>],
arch: &'a str,
ignores: &[Regex],
cb: &'a impl AsyncFn(Event),
) -> Result<Vec<OmaSourceEntry<'a>>, SourcesListError> {
let mut res = vec![];
for p in paths {
match SourcesList::new(p) {
Ok(s) => match s.entries {
SourceListType::SourceLine(source_list_line_style) => {
for source in source_list_line_style.0 {
if let SourceLine::Entry(entry) = source
&& entry.enabled
{
res.push(OmaSourceEntry::new(entry, arch));
}
}
}
SourceListType::Deb822(source_list_deb822) => {
for source in source_list_deb822.entries.into_iter().filter(|s| s.enabled) {
res.push(OmaSourceEntry::new(source, arch));
}
}
},
Err(e) => match e {
SourcesListError::UnknownFile { path } => {
let Some(file_name) = path.file_name() else {
cb(Event::SourceListFileNotSupport { path }).await;
continue;
};
if ignores
.iter()
.any(|re| re.is_match(&file_name.to_string_lossy()).unwrap_or(false))
{
debug!("File {:?} matches ignore list.", file_name);
continue;
}
cb(Event::SourceListFileNotSupport { path }).await;
}
e => return Err(e),
},
}
}
Ok(res)
}
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum OmaSourceEntryFrom {
Http,
Local,
}
impl<'a> OmaSourceEntry<'a> {
pub fn new(source: SourceEntry, arch: &'a str) -> Self {
Self {
source,
arch,
url: OnceCell::new(),
suite: OnceCell::new(),
dist_path: OnceCell::new(),
from: OnceCell::new(),
}
}
pub fn from(&self) -> Result<&OmaSourceEntryFrom, RefreshError> {
self.from.get_or_try_init(|| {
let url = Url::parse(self.url())
.map_err(|_| RefreshError::InvalidUrl(self.url().to_string()))?;
match url.scheme() {
"file" => Ok(OmaSourceEntryFrom::Local),
"http" | "https" => Ok(OmaSourceEntryFrom::Http),
x => Err(RefreshError::UnsupportedProtocol(x.to_string())),
}
})
}
pub fn components(&self) -> &[String] {
&self.source.components
}
pub fn archs(&self) -> &Option<Vec<String>> {
&self.source.archs
}
pub fn trusted(&self) -> bool {
self.source.trusted
}
pub fn signed_by(&self) -> &Option<Signature> {
&self.source.signed_by
}
pub fn url(&self) -> &str {
self.url
.get_or_init(|| self.source.url.replace("$(ARCH)", self.arch))
}
pub fn is_flat(&self) -> bool {
self.components().is_empty()
}
pub fn suite(&self) -> &str {
self.suite
.get_or_init(|| self.source.suite.replace("$(ARCH)", self.arch))
}
pub fn is_source(&self) -> bool {
self.source.source
}
pub fn dist_path(&self) -> &str {
self.dist_path.get_or_init(|| {
let suite = self.suite();
let url = self.url();
if self.is_flat() {
if suite == "/" {
if !url.ends_with('/') {
format!("{url}{suite}")
} else {
url.to_string()
}
} else {
concat_url_only_check_once_slash(url, suite)
}
} else {
self.source.dist_path()
}
})
}
pub fn get_download_file_name(
&self,
file_name: Option<&str>,
replacer: &DatabaseFilenameReplacer,
) -> Result<String, RefreshError> {
let url = if let Some(file_name) = file_name {
Cow::Owned(concat_url_only_check_once_slash(
self.dist_path(),
file_name,
))
} else {
self.dist_path().into()
};
replacer.replace(&url)
}
#[inline]
pub fn get_download_url(&self, file_name: &str) -> String {
concat_url(self.dist_path(), file_name)
}
pub fn get_human_download_url(&self, file_name: Option<&str>) -> Result<String, RefreshError> {
let url = self.url();
let url = Url::parse(url).map_err(|_| RefreshError::InvalidUrl(url.to_string()))?;
let host = url.host_str();
let url = if let Some(host) = host {
host
} else {
url.path()
};
let mut s = format!("{}:{}", url, self.suite());
if let Some(file_name) = file_name {
s.push(' ');
s.push_str(file_name);
}
Ok(s)
}
}
#[derive(Debug)]
pub struct MirrorSources<'a>(pub Vec<MirrorSource<'a>>);
#[derive(Debug)]
pub struct MirrorSource<'a> {
pub sources: Vec<&'a OmaSourceEntry<'a>>,
release_file_name: OnceCell<String>,
auth: Option<&'a Authenticator>,
}
impl MirrorSource<'_> {
pub fn set_release_file_name(&self, file_name: String) {
self.release_file_name
.set(file_name)
.expect("Release file name was init");
}
pub fn dist_path(&self) -> &str {
self.sources.first().unwrap().dist_path()
}
#[cfg(feature = "aosc")]
pub fn suite(&self) -> &str {
self.sources.first().unwrap().suite()
}
#[inline]
pub fn from(&self) -> Result<&OmaSourceEntryFrom, RefreshError> {
self.sources.first().unwrap().from()
}
#[inline]
pub fn get_human_download_message(
&self,
file_name: Option<&str>,
) -> Result<String, RefreshError> {
self.sources
.first()
.unwrap()
.get_human_download_url(file_name)
}
#[inline]
pub fn get_download_file_name(
&self,
file_name: Option<&str>,
replacer: &DatabaseFilenameReplacer,
) -> Result<String, RefreshError> {
self.sources
.first()
.unwrap()
.get_download_file_name(file_name, replacer)
}
#[inline]
pub fn get_download_url(&self, file_name: &str) -> String {
self.sources.first().unwrap().get_download_url(file_name)
}
pub fn signed_by(&self) -> Option<&Signature> {
self.sources.iter().find_map(|x| x.signed_by().as_ref())
}
pub fn url(&self) -> &str {
self.sources.first().unwrap().url()
}
pub fn is_flat(&self) -> bool {
self.sources.first().unwrap().is_flat()
}
pub fn trusted(&self) -> bool {
self.sources.iter().any(|x| x.trusted())
}
pub fn file_name(&self) -> Option<&str> {
self.release_file_name.get().map(|x| x.as_str())
}
pub fn auth(&self) -> Option<&Authenticator> {
self.auth
}
pub async fn fetch(
&self,
client: &Client,
replacer: &DatabaseFilenameReplacer,
index: usize,
total: usize,
download_dir: &Path,
callback: &impl AsyncFn(Event),
) -> Result<(), RefreshError> {
match self.from()? {
OmaSourceEntryFrom::Http => {
self.fetch_http_release(client, replacer, index, total, download_dir, callback)
.await
}
OmaSourceEntryFrom::Local => {
self.fetch_local_release(replacer, index, total, download_dir, callback)
.await
}
}
}
async fn fetch_http_release(
&self,
client: &Client,
replacer: &DatabaseFilenameReplacer,
index: usize,
total: usize,
download_dir: &Path,
callback: impl AsyncFn(Event),
) -> Result<(), RefreshError> {
let msg = self.get_human_download_message(None)?;
callback(Event::DownloadEvent(oma_fetch::Event::NewProgressSpinner {
index,
total,
msg,
}))
.await;
let mut url = self.get_download_url("InRelease");
let mut is_release = false;
let resp = match self.send_request(client, &url, Method::GET).await {
Ok(resp) => resp,
Err(e) if e.status().is_some_and(|e| e == StatusCode::NOT_FOUND) => {
url = self.get_download_url("Release");
let resp = self.send_request(client, &url, Method::GET).await;
if resp.is_err() && self.is_flat() {
callback(Event::DownloadEvent(oma_fetch::Event::ProgressDone(index))).await;
return Ok(());
}
is_release = true;
callback(Event::DownloadEvent(oma_fetch::Event::ProgressDone(index))).await;
resp.map_err(|e| SingleDownloadError::ReqwestError { source: e })
.map_err(|e| RefreshError::DownloadFailed(Some(e)))?
}
Err(e) => {
return Err(RefreshError::DownloadFailed(Some(
SingleDownloadError::ReqwestError { source: e },
)));
}
};
callback(Event::DownloadEvent(oma_fetch::Event::ProgressDone(index))).await;
let file_name = if is_release {
self.get_download_file_name(Some("Release"), replacer)?
} else {
self.get_download_file_name(Some("InRelease"), replacer)?
};
self.download_file(&file_name, resp, index, total, download_dir, &callback)
.await
.map_err(|e| RefreshError::DownloadFailed(Some(e)))?;
self.set_release_file_name(file_name);
if is_release && !self.trusted() {
let url = self.get_download_url("Release.gpg");
let request = build_request_with_basic_auth(
client,
Method::GET,
&self
.auth()
.map(|x| (x.login.to_string(), x.password.to_string())),
&url,
);
let resp = request
.send()
.await
.and_then(|resp| resp.error_for_status())
.map_err(|e| SingleDownloadError::ReqwestError { source: e })
.map_err(|e| RefreshError::DownloadFailed(Some(e)))?;
let file_name = self.get_download_file_name(Some("Release.gpg"), replacer)?;
self.download_file(&file_name, resp, index, total, download_dir, &callback)
.await
.map_err(|e| RefreshError::DownloadFailed(Some(e)))?;
}
Ok(())
}
async fn send_request(
&self,
client: &Client,
url: &str,
method: Method,
) -> Result<Response, oma_fetch::reqwest::Error> {
let request = build_request_with_basic_auth(
client,
method,
&self
.auth()
.map(|x| (x.login.to_string(), x.password.to_string())),
url,
);
let resp = send_request(url, request).await?;
Ok(resp)
}
async fn download_file(
&self,
file_name: &str,
mut resp: Response,
index: usize,
total: usize,
download_dir: &Path,
callback: &impl AsyncFn(Event),
) -> std::result::Result<(), SingleDownloadError> {
let total_size = content_length(&resp);
callback(Event::DownloadEvent(oma_fetch::Event::NewProgressBar {
index,
total,
msg: self.get_human_download_message(Some(file_name)).unwrap(),
size: total_size,
}))
.await;
let mut f = File::create(download_dir.join(file_name))
.await
.map_err(|e| SingleDownloadError::Create { source: e })?;
while let Some(chunk) = resp
.chunk()
.await
.map_err(|e| SingleDownloadError::ReqwestError { source: e })?
{
callback(Event::DownloadEvent(oma_fetch::Event::ProgressInc {
index,
size: chunk.len() as u64,
}))
.await;
f.write_all(&chunk)
.await
.map_err(|e| SingleDownloadError::Write { source: e })?;
}
f.shutdown()
.await
.map_err(|e| SingleDownloadError::Flush { source: e })?;
callback(Event::DownloadEvent(oma_fetch::Event::ProgressDone(index))).await;
Ok(())
}
async fn fetch_local_release(
&self,
replacer: &DatabaseFilenameReplacer,
index: usize,
total: usize,
download_dir: &Path,
callback: &impl AsyncFn(Event),
) -> Result<(), RefreshError> {
let dist_path_with_protocol = self.dist_path();
let dist_path = dist_path_with_protocol
.strip_prefix("file:")
.unwrap_or(dist_path_with_protocol);
let dist_path = Path::new(dist_path);
let mut name = None;
let msg = self.get_human_download_message(None)?;
callback(Event::DownloadEvent(oma_fetch::Event::NewProgressSpinner {
index,
total,
msg,
}))
.await;
let mut is_release = false;
for (index, entry) in ["InRelease", "Release"].iter().enumerate() {
let p = dist_path.join(entry);
let file_name = self.get_download_file_name(Some(entry), replacer)?;
let dst = download_dir.join(&file_name);
if p.exists() {
if dst.exists() {
debug!("get_release_file: Removing {} ...", dst.display());
fs::remove_file(&dst)
.await
.map_err(|e| RefreshError::OperateFile(dst.clone(), e))?;
}
debug!("get_release_file: Symlinking {} ...", dst.display());
fs::symlink(p, &dst)
.await
.map_err(|e| RefreshError::OperateFile(dst.clone(), e))?;
if index == 1 {
is_release = true;
}
name = Some(file_name);
break;
}
}
if name.is_none() && self.is_flat() {
return Ok(());
}
if is_release {
let p = dist_path.join("Release.gpg");
let file_name = self.get_download_file_name(Some("Release.gpg"), replacer)?;
let dst = download_dir.join(&file_name);
if p.exists() {
if dst.exists() {
fs::remove_file(&dst)
.await
.map_err(|e| RefreshError::OperateFile(dst.clone(), e))?;
}
fs::symlink(p, download_dir.join(file_name))
.await
.map_err(|e| RefreshError::OperateFile(dst.clone(), e))?;
}
}
callback(Event::DownloadEvent(oma_fetch::Event::ProgressDone(index))).await;
let name = name.ok_or_else(|| RefreshError::NoInReleaseFile(self.url().to_string()))?;
self.set_release_file_name(name);
Ok(())
}
}
impl<'a> MirrorSources<'a> {
pub fn from_sourcelist(
sourcelist: &'a [OmaSourceEntry<'a>],
replacer: &DatabaseFilenameReplacer,
auth_config: Option<&'a AuthConfig>,
) -> Result<Self, RefreshError> {
let mut map: HashMap<String, Vec<&OmaSourceEntry>> =
HashMap::with_hasher(ahash::RandomState::new());
if sourcelist.is_empty() {
return Err(RefreshError::SourceListsEmpty);
}
for source in sourcelist {
map.entry(source.get_download_file_name(None, replacer)?)
.or_default()
.push(source);
}
let mut res = vec![];
for (_, v) in map {
let url = v[0].url();
let auth = auth_config.and_then(|auth| auth.find(url));
res.push(MirrorSource {
sources: v,
release_file_name: OnceCell::new(),
auth,
});
}
Ok(Self(res))
}
pub async fn fetch_all_release(
&self,
client: &Client,
replacer: &DatabaseFilenameReplacer,
download_dir: &Path,
threads: usize,
callback: &impl AsyncFn(Event),
) -> Vec<Result<(), RefreshError>> {
let tasks = self.0.iter().enumerate().map(|(index, m)| {
m.fetch(
client,
replacer,
index,
self.0.len(),
download_dir,
&callback,
)
});
futures::stream::iter(tasks)
.buffer_unordered(threads)
.collect::<Vec<_>>()
.await
}
}
#[test]
fn test_ose() {
use oma_utils::dpkg::dpkg_arch;
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:///debs/".to_string(),
suite: "/".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
assert_eq!(ose.url(), "file:///debs/");
assert_eq!(ose.dist_path(), "file:///debs/");
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:///debs/".to_string(),
suite: "./".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
assert_eq!(ose.url(), "file:///debs/");
assert_eq!(ose.dist_path(), "file:///debs/./");
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:/debs/".to_string(),
suite: "/".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
assert_eq!(ose.url(), "file:/debs/");
assert_eq!(ose.dist_path(), "file:/debs/");
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:/debs".to_string(),
suite: "/".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
assert_eq!(ose.url(), "file:/debs");
assert_eq!(ose.dist_path(), "file:/debs/");
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:/debs/".to_string(),
suite: "./././".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
assert_eq!(ose.url(), "file:/debs/");
assert_eq!(ose.dist_path(), "file:/debs/./././");
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:/debs/".to_string(),
suite: ".//".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
assert_eq!(ose.url(), "file:/debs/");
assert_eq!(ose.dist_path(), "file:/debs/.//");
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:/debs/".to_string(),
suite: "//".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
assert_eq!(ose.url(), "file:/debs/");
assert_eq!(ose.dist_path(), "file:/debs///");
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:/./debs/".to_string(),
suite: "./".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
assert_eq!(ose.url(), "file:/./debs/");
assert_eq!(ose.dist_path(), "file:/./debs/./");
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:/usr/../debs/".to_string(),
suite: "./".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
assert_eq!(ose.url(), "file:/usr/../debs/");
assert_eq!(ose.dist_path(), "file:/usr/../debs/./");
}
#[test]
fn test_url_encode_plus() {
let replacer = DatabaseFilenameReplacer::new().unwrap();
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "https://repo.aosc.io/debs/".to_string(),
suite: "x264-0+git20240305".to_string(),
components: vec!["main".to_string()],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = oma_utils::dpkg::dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
let file_name = ose
.get_download_file_name(Some("InRelease"), &replacer)
.unwrap();
assert_eq!(
file_name,
"repo.aosc.io_debs_dists_x264-0%252bgit20240305_InRelease"
);
}
#[test]
fn test_dot() {
let replacer = DatabaseFilenameReplacer::new().unwrap();
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "https://ci.deepin.com/repo/obs/deepin:/CI:/TestingIntegration:/test-integration-pr-1537/testing".to_string(),
suite: "./".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = oma_utils::dpkg::dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
let file_name = ose
.get_download_file_name(Some("Packages"), &replacer)
.unwrap();
assert_eq!(
file_name,
"ci.deepin.com_repo_obs_deepin:_CI:_TestingIntegration:_test-integration-pr-1537_testing_._Packages"
);
}
#[test]
fn test_encode_underline() {
let replacer = DatabaseFilenameReplacer::new().unwrap();
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "https://repo.aosc.io/debs/".to_string(),
suite: "xorg-server-21.1.13-hyperv_drm-fix".to_string(),
components: vec!["main".to_string()],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = oma_utils::dpkg::dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
let file_name = ose
.get_download_file_name(Some("InRelease"), &replacer)
.unwrap();
assert_eq!(
file_name,
"repo.aosc.io_debs_dists_xorg-server-21.1.13-hyperv%5fdrm-fix_InRelease"
);
}
#[test]
fn test_file_protocol_translate() {
let replacer = DatabaseFilenameReplacer::new().unwrap();
let s1 = "file:/debs";
let s2 = "file:///debs";
let res1 = replacer.replace(s1).unwrap();
let res2 = replacer.replace(s2).unwrap();
assert_eq!(res1, "_debs");
assert_eq!(res1, res2);
}
#[test]
fn test_flat_repo_file_name_1() {
let replacer = DatabaseFilenameReplacer::new().unwrap();
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:///././debs/".to_string(),
suite: "./".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = oma_utils::dpkg::dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
let file_name = ose
.get_download_file_name(Some("Packages"), &replacer)
.unwrap();
assert_eq!(file_name, "_._._debs_._Packages");
}
#[test]
fn test_flat_repo_file_name_2() {
let replacer = DatabaseFilenameReplacer::new().unwrap();
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:///debs".to_string(),
suite: "/".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = oma_utils::dpkg::dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
let file_name = ose
.get_download_file_name(Some("Packages"), &replacer)
.unwrap();
assert_eq!(file_name, "_debs_Packages");
}
#[test]
fn test_flat_repo_file_name_3() {
let replacer = DatabaseFilenameReplacer::new().unwrap();
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:///debs/".to_string(),
suite: "./".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = oma_utils::dpkg::dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
let file_name = ose
.get_download_file_name(Some("Packages"), &replacer)
.unwrap();
assert_eq!(file_name, "_debs_._Packages");
}
#[test]
fn test_flat_repo_file_name_4() {
let replacer = DatabaseFilenameReplacer::new().unwrap();
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:///debs///".to_string(),
suite: "./".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let arch = oma_utils::dpkg::dpkg_arch("/").unwrap();
let ose = OmaSourceEntry::new(entry, &arch);
let res = ose
.get_download_file_name(Some("Packages"), &replacer)
.unwrap();
assert_eq!(res, "_debs___._Packages");
let entry = SourceEntry {
enabled: true,
source: false,
options: vec![],
url: "file:///debs///".to_string(),
suite: ".///".to_string(),
components: vec![],
is_deb822: false,
archs: None,
signed_by: None,
trusted: false,
};
let ose = OmaSourceEntry::new(entry, &arch);
let res = ose
.get_download_file_name(Some("Packages"), &replacer)
.unwrap();
assert_eq!(res, "_debs___.___Packages");
}