Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-view/src/job.rs')
| -rw-r--r-- | helix-view/src/job.rs | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/helix-view/src/job.rs b/helix-view/src/job.rs new file mode 100644 index 00000000..b709bfb6 --- /dev/null +++ b/helix-view/src/job.rs @@ -0,0 +1,92 @@ +use crate::compositor::Compositor; +use crate::Editor; + +use futures_util::future::{self, BoxFuture, Future, FutureExt}; +use futures_util::stream::{FuturesUnordered, StreamExt}; + +pub type Callback = Box<dyn FnOnce(&mut Editor, &mut Compositor) + Send>; +pub type JobFuture = BoxFuture<'static, anyhow::Result<Option<Callback>>>; + +pub struct Job { + pub future: BoxFuture<'static, anyhow::Result<Option<Callback>>>, + /// Do we need to wait for this job to finish before exiting? + pub wait: bool, +} + +#[derive(Default)] +pub struct Jobs { + pub futures: FuturesUnordered<JobFuture>, + /// These are the ones that need to complete before we exit. + pub wait_futures: FuturesUnordered<JobFuture>, +} + +impl Job { + pub fn new<F: Future<Output = anyhow::Result<()>> + Send + 'static>(f: F) -> Self { + Self { + future: f.map(|r| r.map(|()| None)).boxed(), + wait: false, + } + } + + pub fn with_callback<F: Future<Output = anyhow::Result<Callback>> + Send + 'static>( + f: F, + ) -> Self { + Self { + future: f.map(|r| r.map(Some)).boxed(), + wait: false, + } + } + + pub fn wait_before_exiting(mut self) -> Self { + self.wait = true; + self + } +} + +impl Jobs { + pub fn new() -> Self { + Self::default() + } + + pub fn spawn<F: Future<Output = anyhow::Result<()>> + Send + 'static>(&mut self, f: F) { + self.add(Job::new(f)); + } + + pub fn callback<F: Future<Output = anyhow::Result<Callback>> + Send + 'static>( + &mut self, + f: F, + ) { + self.add(Job::with_callback(f)); + } + + pub fn handle_callback( + &self, + editor: &mut Editor, + compositor: &mut Compositor, + call: anyhow::Result<Option<Callback>>, + ) { + match call { + Ok(None) => {} + Ok(Some(call)) => { + call(editor, compositor); + } + Err(e) => { + editor.set_error(format!("Async job failed: {}", e)); + } + } + } + + pub fn add(&self, j: Job) { + if j.wait { + self.wait_futures.push(j.future); + } else { + self.futures.push(j.future); + } + } + + /// Blocks until all the jobs that need to be waited on are done. + pub async fn finish(&mut self) { + let wait_futures = std::mem::take(&mut self.wait_futures); + wait_futures.for_each(|_| future::ready(())).await + } +} |