Skip to main content

onetaskgraph_plugin_api/
guard.rs

1//! What a source asks before each write request it sends while the engine owns a create there.
2//!
3//! A copy that creates an item holds an intent on the item it copies, under a lease, until it
4//! links the counterpart. Each destination write it sends after its create request has to renew
5//! that lease, and to stop once another attempt holds it — including the several requests one
6//! plugin call can send: a GitHub create is `createIssue` and then the card's fields, its
7//! relationships and, should those fail, the delete that takes the issue back. The engine cannot
8//! see those requests, so it installs a [`WriteGuard`] for the length of the call and the source
9//! asks it, through [`<dyn WriteGuard>::check`], immediately before sending each one.
10//!
11//! The guard is task-local, so a source that never asks — every one that predates this — writes
12//! exactly as it did, and one that asks outside a guarded call is answered at once.
13
14use std::future::Future;
15use std::sync::Arc;
16
17use crate::SourceError;
18
19tokio::task_local! {
20    static GUARD: Arc<dyn WriteGuard>;
21}
22
23/// Renews the engine's hold on the creates a guarded call writes for, before each request.
24#[async_trait::async_trait]
25pub trait WriteGuard: Send + Sync {
26    /// Renew, and answer whether the next write request may be sent: an error is the reason it
27    /// may not — another attempt holds the create — and every later ask answers it again, so a
28    /// source sends nothing more, not even a write taking back what it made.
29    ///
30    /// # Errors
31    ///
32    /// The refusal the source is to fail its call with, sending nothing more.
33    async fn before_write(&self) -> Result<(), SourceError>;
34}
35
36impl dyn WriteGuard {
37    /// Run `call` with `guard` answering every [`<dyn WriteGuard>::check`] made inside it.
38    pub async fn scope<F: Future>(guard: Arc<dyn WriteGuard>, call: F) -> F::Output {
39        GUARD.scope(guard, call).await
40    }
41
42    /// Ask the guard of the call in progress, if any, whether a write request may be sent now —
43    /// a source calls this immediately before sending each write request but its create.
44    ///
45    /// # Errors
46    ///
47    /// The guard's refusal; with no guard installed, never.
48    pub async fn check() -> Result<(), SourceError> {
49        match GUARD.try_with(Arc::clone) {
50            Ok(guard) => guard.before_write().await,
51            Err(_) => Ok(()),
52        }
53    }
54}