feat: add repo cloning

Signed-off-by: sisungo <[email protected]>
This commit is contained in:
2026-06-23 20:59:41 +08:00
parent e8d2c85a69
commit b6dbfd12be
5 changed files with 94 additions and 6 deletions
+1
View File
@@ -12,3 +12,4 @@ anyhow = "1"
clap = { version = "4", features = ["derive"] }
packie = { path = "../" }
rust-i18n = "4"
indicatif = "0.18"
+18
View File
@@ -1,5 +1,6 @@
use anyhow::anyhow;
use clap::Parser;
use indicatif::ProgressBar;
use packie::repo::RepoServeDir;
use std::path::PathBuf;
@@ -11,9 +12,26 @@ pub struct Cli {
/// Add package(s) to the repository
#[arg(long, short)]
add_package: Option<Vec<PathBuf>>,
/// Clone or pull remote repository
#[arg(long, short)]
clone: Option<String>,
}
pub fn main(cli: Cli) -> anyhow::Result<()> {
if let Some(clone) = cli.clone {
let progress_bar = ProgressBar::new(0);
RepoServeDir::clone(&clone.parse()?, &cli.repo, |pkg, url, sum, total| {
progress_bar.set_length(total);
progress_bar.set_position(sum);
if let Some(pkg) = pkg {
progress_bar.set_prefix(pkg);
}
progress_bar.set_message(url.to_string());
})
.map_err(|x| anyhow!("{x}"))?;
}
let mut repo = RepoServeDir::open(cli.repo)?;
for add_package in cli.add_package.into_iter().flatten() {
+4
View File
@@ -83,6 +83,10 @@ pub struct PkgManifest {
#[serde(default)]
pub dependencies: Vec<PkgSpec>,
/// Package provisions.
#[serde(default)]
pub provides: Vec<String>,
/// Links.
#[serde(default)]
pub links: Vec<LinkDescription>,
+30 -4
View File
@@ -121,7 +121,32 @@ impl RepoDb {
)?.query_one(params![pkg_ident.name, pkg_ident.version.to_string(), pkg_ident.arch], map_repo_package)
}
pub fn select_package_by_pkgname(&mut self) {}
pub fn select_package_by_pkgname(
&mut self,
pkgname: &str,
) -> rusqlite::Result<Vec<RepoPackage>> {
let mut stmt = self
.0
.prepare_cached(r#"SELECT * FROM "package" WHERE "pkg_name" = ?1"#)?;
let mapped_rows = stmt.query_map(params![pkgname], map_repo_package)?;
let mut ret = Vec::with_capacity(16);
for mr in mapped_rows {
ret.push(mr?);
}
Ok(ret)
}
pub fn for_each_package<E: From<rusqlite::Error>>(
&mut self,
mut f: impl FnMut(RepoPackage) -> Result<(), E>,
) -> Result<(), E> {
let mut stmt = self.0.prepare_cached("SELECT * FROM \"package\"")?;
let mapped_rows = stmt.query_map(params![], map_repo_package)?;
for mr in mapped_rows {
f(mr?)?;
}
Ok(())
}
pub fn insert_package(&mut self, package: &RepoPackage) -> rusqlite::Result<()> {
let pkg_manifest = serde_json::to_string(&package.pkg_manifest)
@@ -131,9 +156,9 @@ impl RepoDb {
r#"
INSERT INTO "package"(
"pkg_name", "pkg_version", "pkg_arch", "pkg_manifest",
"download_size", "installed_size"
"download_size", "installed_size", "checksum"
)
VALUES(?1, ?2, ?3, ?4, ?5, ?6)
VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7)
"#,
)?
.execute(params![
@@ -142,7 +167,8 @@ impl RepoDb {
package.pkg_manifest.arch,
pkg_manifest,
package.download_size as i64,
package.installed_size as i64
package.installed_size as i64,
package.checksum
])
.map(|_| ())
}
+41 -2
View File
@@ -1,9 +1,10 @@
use crate::{
common::checksum::DEFAULT_HASHER,
package::Package,
repo::{RepoDb, RepoPackage},
repo::{RepoDb, RepoPackage, download::Download},
};
use std::path::{Path, PathBuf};
use url::Url;
/// A helper type to deal with repository serve directories.
#[derive(Debug)]
@@ -16,11 +17,49 @@ impl RepoServeDir {
pub fn open<P: Into<PathBuf>>(path: P) -> rusqlite::Result<Self> {
let path = path.into();
Ok(Self {
db: RepoDb::open_rw(&path)?,
db: RepoDb::open_rw(path.join(RepoDb::FILENAME))?,
path,
})
}
pub fn clone<P: Into<PathBuf>>(
url: &Url,
path: P,
mut on_event: impl FnMut(Option<String>, Url, u64, u64),
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let path = path.into();
std::fs::create_dir_all(&path)?;
let repo_db_url = url.join(RepoDb::FILENAME)?;
Download::new(repo_db_url.to_string(), path.join(RepoDb::FILENAME))
.on_progress(|sum, total| on_event(None, repo_db_url.clone(), sum, total))
.run()?;
let mut db = RepoDb::open_ro(path.join(RepoDb::FILENAME))?;
db.for_each_package::<Box<dyn std::error::Error + Send + Sync>>(|pkg| {
let pkg_ident = pkg.pkg_manifest.pkg_ident();
let standard_filename = format!("{}.pkg", pkg_ident.to_string());
let pkgfile_url = url.join(&standard_filename)?;
let pkgfile_path = path.join(&standard_filename);
if crate::common::checksum::verify_file(&pkgfile_path, &pkg.checksum)
.unwrap_or_default()
{
return Ok(());
}
Download::new(pkgfile_url.to_string(), pkgfile_path)
.on_progress(|sum, total| {
on_event(Some(pkg_ident.to_string()), pkgfile_url.clone(), sum, total)
})
.run()
.map_err::<Box<dyn std::error::Error + Send + Sync>, _>(|e| {
Box::from(format!("failed to download \"{pkgfile_url}\": {e}"))
})?;
Ok(())
})?;
Ok(())
}
/// Add a package to the repository.
pub fn add_package<P: AsRef<Path>>(
&mut self,