mod conn; pub mod entities; mod migrator; use sea_orm::{ ActiveModelTrait, ColumnTrait, ConnectOptions, Database, DatabaseConnection, DeleteResult, DeriveActiveEnum, EntityTrait, EnumIter, InsertResult, ModelTrait, NotSet, PaginatorTrait, QueryFilter, QueryOrder, Set, }; use sea_orm_migration::MigratorTrait; use serde::{Deserialize, Serialize}; pub use entities::{prelude::*, *}; use migrator::Migrator; type Result = std::result::Result; #[derive(EnumIter, DeriveActiveEnum, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] #[sea_orm(rs_type = "String", db_type = "String(Some(6))")] pub enum PackageDepend { #[sea_orm(string_value = "depend")] Depend, #[sea_orm(string_value = "make")] Make, #[sea_orm(string_value = "check")] Check, #[sea_orm(string_value = "opt")] Opt, } #[derive(Serialize)] pub struct FullPackage { #[serde(flatten)] entry: package::Model, licenses: Vec, groups: Vec, replaces: Vec, provides: Vec, depends: Vec<(PackageDepend, String)>, files: Vec, } #[derive(Clone, Debug)] pub struct RieterDb { pub conn: DatabaseConnection, } impl RieterDb { pub async fn connect>(opt: C) -> Result { let db = Database::connect(opt).await?; Migrator::up(&db, None).await?; Ok(Self { conn: db }) } pub async fn repos(&self, per_page: u64, page: u64) -> Result<(u64, Vec)> { let paginator = Repo::find() .order_by_asc(repo::Column::Id) .paginate(&self.conn, per_page); let repos = paginator.fetch_page(page).await?; let total_pages = paginator.num_pages().await?; Ok((total_pages, repos)) } pub async fn repo(&self, id: i32) -> Result> { repo::Entity::find_by_id(id).one(&self.conn).await } pub async fn repo_by_name(&self, name: &str) -> Result> { Repo::find() .filter(repo::Column::Name.eq(name)) .one(&self.conn) .await } pub async fn insert_repo( &self, name: &str, description: Option<&str>, ) -> Result> { let model = repo::ActiveModel { id: NotSet, name: Set(String::from(name)), description: Set(description.map(String::from)), }; Repo::insert(model).exec(&self.conn).await } pub async fn packages(&self, per_page: u64, page: u64) -> Result<(u64, Vec)> { let paginator = Package::find() .order_by_asc(package::Column::Id) .paginate(&self.conn, per_page); let packages = paginator.fetch_page(page).await?; let total_pages = paginator.num_pages().await?; Ok((total_pages, packages)) } pub async fn package(&self, id: i32) -> Result> { package::Entity::find_by_id(id).one(&self.conn).await } pub async fn package_by_fields( &self, repo_id: i32, name: &str, version: Option<&str>, arch: &str, ) -> Result> { let mut query = Package::find() .filter(package::Column::RepoId.eq(repo_id)) .filter(package::Column::Name.eq(name)) .filter(package::Column::Arch.eq(arch)); if let Some(version) = version { query = query.filter(package::Column::Version.eq(version)); } query.one(&self.conn).await } pub async fn delete_packages_with_arch( &self, repo_id: i32, arch: &str, ) -> Result { Package::delete_many() .filter(package::Column::RepoId.eq(repo_id)) .filter(package::Column::Arch.eq(arch)) .exec(&self.conn) .await } pub async fn insert_package( &self, repo_id: i32, pkg: crate::repo::package::Package, ) -> Result<()> { let info = pkg.info; let model = package::ActiveModel { id: NotSet, repo_id: Set(repo_id), base: Set(info.base), name: Set(info.name), version: Set(info.version), arch: Set(info.arch), size: Set(info.size), c_size: Set(info.csize), description: Set(info.description), url: Set(info.url), build_date: Set(info.build_date), packager: Set(info.packager), pgp_sig: Set(info.pgpsig), pgp_sig_size: Set(info.pgpsigsize), sha256_sum: Set(info.sha256sum), }; let pkg_entry = model.insert(&self.conn).await?; // Insert all the related tables PackageLicense::insert_many(info.licenses.iter().map(|s| package_license::ActiveModel { package_id: Set(pkg_entry.id), value: Set(s.to_string()), })) .on_empty_do_nothing() .exec(self) .await?; PackageGroup::insert_many(info.groups.iter().map(|s| package_group::ActiveModel { package_id: Set(pkg_entry.id), value: Set(s.to_string()), })) .on_empty_do_nothing() .exec(self) .await?; PackageReplaces::insert_many(info.replaces.iter().map(|s| package_replaces::ActiveModel { package_id: Set(pkg_entry.id), value: Set(s.to_string()), })) .on_empty_do_nothing() .exec(self) .await?; PackageConflicts::insert_many(info.conflicts.iter().map(|s| { package_conflicts::ActiveModel { package_id: Set(pkg_entry.id), value: Set(s.to_string()), } })) .on_empty_do_nothing() .exec(self) .await?; PackageProvides::insert_many(info.provides.iter().map(|s| package_provides::ActiveModel { package_id: Set(pkg_entry.id), value: Set(s.to_string()), })) .on_empty_do_nothing() .exec(self) .await?; PackageFile::insert_many(pkg.files.iter().map(|s| package_file::ActiveModel { package_id: Set(pkg_entry.id), value: Set(s.display().to_string()), })) .on_empty_do_nothing() .exec(self) .await?; let deps = info .depends .iter() .map(|d| (PackageDepend::Depend, d)) .chain(info.makedepends.iter().map(|d| (PackageDepend::Make, d))) .chain(info.checkdepends.iter().map(|d| (PackageDepend::Check, d))) .chain(info.optdepends.iter().map(|d| (PackageDepend::Opt, d))) .map(|(t, s)| package_depends::ActiveModel { package_id: Set(pkg_entry.id), r#type: Set(t), value: Set(s.to_string()), }); PackageDepends::insert_many(deps) .on_empty_do_nothing() .exec(self) .await?; Ok(()) } pub async fn full_package(&self, id: i32) -> Result> { if let Some(entry) = self.package(id).await? { let licenses = entry .find_related(PackageLicense) .all(self) .await? .into_iter() .map(|e| e.value) .collect(); let groups = entry .find_related(PackageGroup) .all(self) .await? .into_iter() .map(|e| e.value) .collect(); let replaces = entry .find_related(PackageReplaces) .all(self) .await? .into_iter() .map(|e| e.value) .collect(); let provides = entry .find_related(PackageProvides) .all(self) .await? .into_iter() .map(|e| e.value) .collect(); let depends = entry .find_related(PackageDepends) .all(self) .await? .into_iter() .map(|e| (e.r#type, e.value)) .collect(); let files = entry .find_related(PackageFile) .all(self) .await? .into_iter() .map(|e| e.value) .collect(); Ok(Some(FullPackage { entry, licenses, groups, replaces, provides, depends, files, })) } else { Ok(None) } } }