1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// =============================================================================
// #######
// ### ### F: control.rs
// ## ## ## ## P: AppCore-Runtime
// ## ##
// C: 2026/08/30 05:00:00 by dnettoRaw
// ## ## ## ## U: 2026/08/30 05:00:00 by dnettoRaw
// ########### S: 1.0.2-rc
// =============================================================================
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::{ErrorCode, FileMakerError, Result};
/// Stable operation phase reported at cooperative boundaries.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ProgressPhase {
/// Parsing and expanding a template.
Compile,
/// Binding typed data and patches.
Bind,
/// Expanding bound and repeated element instances.
BindElements,
/// Measuring and resolving geometry.
Layout,
/// Collision/reflow iteration.
Reflow,
/// Preflight inspection.
Preflight,
/// Encoding output.
Export,
}
/// Bounded progress notification.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ProgressEvent {
/// Current phase.
pub phase: ProgressPhase,
/// Completed deterministic work units.
pub completed: u64,
/// Known upper bound, when meaningful.
pub total: Option<u64>,
}
/// Observer invoked synchronously; implementations must return promptly.
pub trait ProgressObserver: Send + Sync {
/// Receives one progress event without influencing compiler behavior.
fn report(&self, event: &ProgressEvent);
}
/// Cheap cloneable cooperative cancellation flag.
#[derive(Clone, Debug, Default)]
pub struct CancellationToken(Arc<AtomicBool>);
impl CancellationToken {
/// Requests cancellation for every clone of this token.
pub fn cancel(&self) {
self.0.store(true, Ordering::Release);
}
/// Returns whether cancellation has been requested.
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.0.load(Ordering::Acquire)
}
/// Converts a cancellation request into the stable controlled error.
pub fn check(&self) -> Result<()> {
if self.is_cancelled() {
return Err(FileMakerError::new(
ErrorCode::Cancelled,
"operation cancelled at a cooperative boundary",
));
}
Ok(())
}
}
/// Cancellation and progress controls shared by one operation pipeline.
#[derive(Clone, Default)]
pub struct OperationControl {
cancellation: CancellationToken,
observer: Option<Arc<dyn ProgressObserver>>,
}
impl OperationControl {
/// Creates controls around the supplied cancellation token.
#[must_use]
pub fn new(cancellation: CancellationToken) -> Self {
Self {
cancellation,
observer: None,
}
}
/// Installs a synchronous progress observer.
#[must_use]
pub fn with_observer(mut self, observer: Arc<dyn ProgressObserver>) -> Self {
self.observer = Some(observer);
self
}
/// Returns the shared cancellation token.
#[must_use]
pub fn cancellation(&self) -> &CancellationToken {
&self.cancellation
}
/// Checks cancellation, then emits a bounded progress event.
pub fn checkpoint(
&self,
phase: ProgressPhase,
completed: u64,
total: Option<u64>,
) -> Result<()> {
self.cancellation.check()?;
if let Some(observer) = &self.observer {
observer.report(&ProgressEvent {
phase,
completed,
total,
});
self.cancellation.check()?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cancellation_is_shared_and_controlled() {
let token = CancellationToken::default();
let clone = token.clone();
token.cancel();
assert_eq!(clone.check().unwrap_err().code(), ErrorCode::Cancelled);
}
}