use std::{
path::{Path, PathBuf},
sync::Arc,
};
use crossbeam_queue::ArrayQueue;
use once_cell::sync::Lazy;
use rayon::prelude::*;
use tokio::sync::{Mutex, mpsc};
use crate::{
Fn::Binary::Command::Index,
Struct::Binary::Command::Entry::Struct as ExecutionOption,
};
pub mod GPG;
pub mod Process;
static GPG_MUTEX:Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
struct ProcessedCommand {
Command:String,
RequiresGpgLock:bool,
RequiresIndexLock:bool,
}
pub async fn Fn(Option:ExecutionOption) {
let ProcessedCommands:Arc<Vec<ProcessedCommand>> = Arc::new(
Option
.Command
.par_iter()
.map(|CommandString| {
let RequiresGpgLock = GPG::Fn(CommandString);
let RequiresIndexLock = Index::Fn(CommandString);
ProcessedCommand { Command:CommandString.clone(), RequiresGpgLock, RequiresIndexLock }
})
.collect(),
);
let TargetDirs:Vec<PathBuf> = Option
.Entry
.into_par_iter()
.filter_map(|CandidatePath| {
if CandidatePath.file_name().is_some_and(|Name| Name == Option.Pattern.as_str()) {
CandidatePath.parent().map(Path::to_path_buf)
} else {
None
}
})
.collect();
if TargetDirs.is_empty() {
return;
}
let (Tx, mut Rx) = mpsc::unbounded_channel::<String>();
let WorkQueue = Arc::new(ArrayQueue::new(TargetDirs.len()));
for Dir in TargetDirs {
WorkQueue
.push(Dir)
.expect("Queue should have enough capacity for all target directories.");
}
let OutputTask = tokio::spawn(async move {
while let Some(Output) = Rx.recv().await {
if !Output.trim().is_empty() {
println!("{}", Output);
}
}
});
let WorkerCount = rayon::current_num_threads();
let mut WorkerHandles = Vec::with_capacity(WorkerCount);
for _ in 0..WorkerCount {
let Queue = Arc::clone(&WorkQueue);
let Commands = Arc::clone(&ProcessedCommands);
let Producer = Tx.clone();
let WorkerHandle = tokio::spawn(async move {
while let Some(Directory) = Queue.pop() {
let DirectoryString = Directory.to_string_lossy();
let mut DirectoryOutput = String::new();
'commands: for Cmd in Commands.iter() {
if Cmd.RequiresIndexLock
&& !Index::Lock::Fn(&DirectoryString).await
{
eprintln!(
"Skipping remaining commands in '{}': git index lock timed out.",
DirectoryString
);
break 'commands;
}
let Result = if Cmd.RequiresGpgLock {
let _GpgLock = GPG_MUTEX.lock().await;
Process::Fn(&Cmd.Command, &DirectoryString).await
} else {
Process::Fn(&Cmd.Command, &DirectoryString).await
};
match Result {
Ok(Output) => DirectoryOutput.push_str(&Output),
Err(Error) => {
eprintln!(
"Error executing command in '{}': {}",
DirectoryString, Error
)
},
}
}
if !DirectoryOutput.trim().is_empty()
&& Producer.send(DirectoryOutput).is_err()
{
break;
}
}
});
WorkerHandles.push(WorkerHandle);
}
for Handle in WorkerHandles {
Handle.await.expect("Worker task panicked.");
}
drop(Tx);
OutputTask.await.expect("Output task panicked.");
}