use crate::load::NcbiFile;
use anyhow::Result;
use futures::stream::StreamExt;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use tokio::sync::{mpsc, Semaphore};
async fn process_tasks(
task_type: String,
mut receiver: mpsc::Receiver<(NcbiFile, tokio::sync::OwnedSemaphorePermit)>,
) -> Result<usize> {
let counter: Arc<AtomicUsize> = Arc::new(AtomicUsize::new(0));
let mut stream = futures::stream::FuturesUnordered::new();
while let Some((task, permit)) = receiver.recv().await {
let task_type_clone = task_type.clone();
let counter_clone = counter.clone();
let task_future = tokio::spawn(async move {
let result = match task_type_clone.as_str() {
"run" => {
let result = task.run().await;
if result.is_ok() {
task.check().await
} else {
result
}
}
"check" => task.check().await,
_ => unreachable!(),
};
drop(permit);
if result.is_ok() {
counter_clone.fetch_add(1, Ordering::SeqCst);
}
result
});
stream.push(task_future);
}
while let Some(result) = stream.next().await {
match result {
Ok(Ok(_)) => {} Ok(Err(e)) => log::error!("Task failed: {}", e),
Err(e) => log::error!("Task panicked or could not be joined: {}", e),
}
}
Ok(counter.load(Ordering::SeqCst))
}
async fn process_assembly_tasks(
site: &str,
group: &str,
data_dir: &PathBuf,
asm_levels: &Vec<&str>,
tx: mpsc::Sender<(NcbiFile, tokio::sync::OwnedSemaphorePermit)>,
semaphore: Arc<Semaphore>,
) -> Result<usize> {
let counter = Arc::new(AtomicUsize::new(0));
let assembly = NcbiFile::from_group(group, data_dir, site).await;
match assembly.run().await {
Ok(_) => {
let result = assembly
.process_summary_and_apply(site, data_dir, asm_levels, |file: NcbiFile| {
let tx_clone = tx.clone();
let counter_clone = counter.clone();
let semaphore_clone = semaphore.clone();
async move {
let permit = semaphore_clone.acquire_owned().await.unwrap();
let _ = tx_clone.send((file, permit)).await;
counter_clone.fetch_add(1, Ordering::SeqCst);
}
})
.await;
if result.is_err() {
log::error!("Error parsing assembly file: {:?}", result);
}
}
Err(e) => {
log::info!("{}", e);
}
}
Ok(counter.load(Ordering::SeqCst))
}
pub async fn run_task(
site: &str,
group: &str,
data_dir: &PathBuf,
asm_levels: &Vec<&str>,
num_threads: usize,
) -> Result<()> {
log::info!("{} {} download file start...", group, site);
let (tx, rx) = mpsc::channel(4096); let semaphore = Arc::new(Semaphore::new(num_threads));
let assembly_tasks =
process_assembly_tasks(site, group, data_dir, asm_levels, tx, semaphore.clone());
let download_handle = process_tasks("run".to_string(), rx);
let (ably_res, down_res) = tokio::join!(assembly_tasks, download_handle);
log::info!(
"{} {} file total count: {}, downloaded: {}",
group,
site,
ably_res?,
down_res?,
);
log::info!("{} {} file finished...", group, site);
Ok(())
}
pub async fn run_check(
site: &str,
group: &str,
data_dir: &PathBuf,
asm_levels: &Vec<&str>,
num_threads: usize,
) -> Result<()> {
log::info!("{} {} check md5 start...", group, site);
let (tx, rx) = mpsc::channel(4096); let semaphore = Arc::new(Semaphore::new(num_threads));
let assembly_tasks =
process_assembly_tasks(site, group, data_dir, asm_levels, tx, semaphore.clone());
let md5_handle = process_tasks("check".to_string(), rx);
let (ably_res, md5_res) = tokio::join!(assembly_tasks, md5_handle);
log::info!(
"{} {} file total count: {}, md5match: {}",
group,
site,
ably_res?,
md5_res?
);
Ok(())
}
pub async fn run_taxo(taxo_dir: &PathBuf) -> Result<()> {
log::info!("download taxonomy...");
let files = [
"taxdump.tar.gz",
];
for url_path in files.iter() {
let ncbi_file = NcbiFile::new_taxo(taxo_dir, &url_path).await;
let result = ncbi_file.run().await;
if result.is_ok() && url_path.to_string() == "taxdump.tar.gz" {
ncbi_file.decompress(taxo_dir).await?;
}
}
log::info!("download taxonomy finished...");
Ok(())
}
pub async fn run_download_file(site: &str, data_dir: &PathBuf, fna_url: &str) -> Result<()> {
let ncbi_file = NcbiFile::from_file(site, data_dir, fna_url).await;
log::info!("{} download file start...", fna_url);
ncbi_file.clear().await;
ncbi_file.run().await?;
ncbi_file.check().await?;
log::info!("{} download file end...", fna_url);
Ok(())
}
pub async fn run_assembly(
site: &str,
group: &str,
asm_levels: &Vec<&str>,
data_dir: &PathBuf,
) -> Result<()> {
let assembly = NcbiFile::from_group(group, data_dir, site).await;
if !assembly.file_exists() {
let _ = assembly.run().await;
}
let total_counter = Arc::new(AtomicUsize::new(0));
let counter = Arc::new(AtomicUsize::new(0));
let result = assembly
.process_summary_and_apply(site, data_dir, asm_levels, |file: NcbiFile| {
let counter_clone = counter.clone();
let total_counter_clone = total_counter.clone();
async move {
total_counter_clone.fetch_add(1, Ordering::SeqCst);
if file.file_exists() {
counter_clone.fetch_add(1, Ordering::SeqCst);
}
}
})
.await;
if result.is_err() {
log::error!("Error parsing assembly file: {:?}", result);
}
let total_count = total_counter.load(Ordering::SeqCst);
let count = counter.load(Ordering::SeqCst);
log::info!(
"{} {} 总文件数: {}, 本地文件数: {}",
group,
site,
total_count,
count
);
Ok(())
}