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}