use super::{RepoCommand, RepoSharedState}; use crate::db; use std::{ collections::HashMap, path::{Path, PathBuf}, sync::{ atomic::{AtomicU32, Ordering}, Arc, Mutex, RwLock, }, }; use sea_orm::{ ActiveModelTrait, ColumnTrait, Condition, ConnectionTrait, DbConn, EntityTrait, JoinType, ModelTrait, NotSet, QueryFilter, QuerySelect, Related, RelationTrait, Set, TransactionTrait, }; use tokio::{ runtime, sync::mpsc::{unbounded_channel, UnboundedSender}, }; use uuid::Uuid; #[derive(Clone)] pub struct Handle { state: Arc, } impl Handle { pub fn start( repos_dir: impl AsRef, conn: DbConn, rt: runtime::Handle, actors: u32, ) -> crate::Result { std::fs::create_dir_all(repos_dir.as_ref())?; let mut repos = HashMap::new(); let repo_ids: Vec = rt.block_on( db::Repo::find() .select_only() .column(db::repo::Column::Id) .into_tuple() .all(&conn), )?; for id in repo_ids { repos.insert(id, Default::default()); } let state = Arc::new(RepoSharedState::new(repos_dir, conn, repos)); for _ in 0..actors { let actor = super::RepoActor::new(rt.clone(), Arc::clone(&state)); std::thread::spawn(|| actor.run()); } Ok(Self { state }) } pub fn random_file_paths(&self) -> [PathBuf; C] { std::array::from_fn(|_| { let uuid: uuid::fmt::Simple = Uuid::new_v4().into(); self.state.repos_dir.join(uuid.to_string()) }) } pub async fn get_or_create_repo(&self, distro: &str, repo: &str) -> crate::Result { let mut repos = self.state.repos.write().await; let distro_id: Option = db::Distro::find() .filter(db::distro::Column::Name.eq(distro)) .select_only() .column(db::distro::Column::Id) .into_tuple() .one(&self.state.conn) .await?; let distro_id = if let Some(id) = distro_id { id } else { let new_distro = db::distro::ActiveModel { id: NotSet, name: Set(distro.to_string()), description: NotSet, }; new_distro.insert(&self.state.conn).await?.id }; let repo_id: Option = db::Repo::find() .filter(db::repo::Column::DistroId.eq(distro_id)) .filter(db::repo::Column::Name.eq(repo)) .select_only() .column(db::repo::Column::Id) .into_tuple() .one(&self.state.conn) .await?; let repo_id = if let Some(id) = repo_id { id } else { let new_repo = db::repo::ActiveModel { id: NotSet, distro_id: Set(distro_id), name: Set(repo.to_string()), description: NotSet, }; let id = new_repo.insert(&self.state.conn).await?.id; tokio::fs::create_dir(self.state.repos_dir.join(id.to_string())).await?; repos.insert(id, Default::default()); id }; Ok(repo_id) } pub async fn queue_pkg(&self, repo: i32, path: PathBuf) { self.state .tx .send(RepoCommand::ParsePkg(repo, path)) .unwrap(); self.state.repos.read().await.get(&repo).inspect(|n| { n.0.fetch_add(1, Ordering::SeqCst); }); } }