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.rs92
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
+ }
+}