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
//! Types used to report and handle compilation progress.
use std::{borrow::Cow, fmt, num::NonZeroUsize, string::String, sync::Arc};
use thiserror::Error;
/// Indicates the current compilation progress.
///
/// All fields are kept private for forwards compatibility and future extension.
/// Use the provided methods to access progress data.
#[derive(Clone, Debug, Default)]
pub struct CompilationProgress {
phase_name: Option<Cow<'static, str>>,
phase_step_count: Option<u64>,
phase_step: Option<u64>,
}
impl CompilationProgress {
/// Creates a new [`CompilationProgress`].
pub fn new(
phase_name: Option<Cow<'static, str>>,
phase_step_count: Option<u64>,
phase_step: Option<u64>,
) -> Self {
Self {
phase_name,
phase_step_count,
phase_step,
}
}
/// Returns the name of the phase currently being executed.
pub fn phase_name(&self) -> Option<&str> {
self.phase_name.as_deref()
}
/// Returns the total number of steps in the current phase, if known.
pub fn phase_step_count(&self) -> Option<u64> {
self.phase_step_count
}
/// Returns the index of the current step within the phase, if known.
pub fn phase_step(&self) -> Option<u64> {
self.phase_step
}
}
/// Error returned when the user requests to abort an expensive computation.
#[derive(Clone, Debug, Error)]
#[error("{reason}")]
pub struct UserAbort {
reason: String,
}
impl UserAbort {
/// Creates a new [`UserAbort`].
pub fn new(reason: impl Into<String>) -> Self {
Self {
reason: reason.into(),
}
}
/// Returns the configured reason.
pub fn reason(&self) -> &str {
&self.reason
}
}
type ProgressCallback =
dyn Fn(CompilationProgress) -> Result<(), UserAbort> + Send + Sync + 'static;
type ReserveSizeCallbackFn = dyn Fn(usize) -> Result<(), UserAbort> + Send + Sync + 'static;
#[derive(Clone)]
struct ReserveSizeCallback {
callback: Arc<ReserveSizeCallbackFn>,
chunk_size: NonZeroUsize,
}
/// Wraps callbacks that can receive compilation progress and output-size notifications.
#[derive(Clone)]
pub struct CompilationProgressCallback {
callback: Arc<ProgressCallback>,
reserve_size_callback: Option<ReserveSizeCallback>,
}
impl CompilationProgressCallback {
/// Create a new callback wrapper.
///
/// The provided callback will be invoked with progress updates during the compilation process,
/// and has to return a `Result<(), UserAbort>`.
///
/// If the callback returns an error, the compilation will be aborted with a `CompileError::Aborted`.
pub fn new<F>(callback: F) -> Self
where
F: Fn(CompilationProgress) -> Result<(), UserAbort> + Send + Sync + 'static,
{
Self {
callback: Arc::new(callback),
reserve_size_callback: None,
}
}
/// Configures a callback for reporting increases in compilation output size.
///
/// Singlepass uses this callback to account for emitted native-code bytes, reporting after
/// at least `chunk_size` bytes.
pub fn with_reserve_size_callback<F>(mut self, callback: F, chunk_size: NonZeroUsize) -> Self
where
F: Fn(usize) -> Result<(), UserAbort> + Send + Sync + 'static,
{
self.reserve_size_callback = Some(ReserveSizeCallback {
callback: Arc::new(callback),
chunk_size,
});
self
}
/// Returns the configured reserve-size reporting chunk size.
pub fn reserve_size_chunk_size(&self) -> Option<NonZeroUsize> {
self.reserve_size_callback
.as_ref()
.map(|callback| callback.chunk_size)
}
/// Reports an increase in compilation output size, in bytes.
///
/// This is a no-op when no reserve-size callback was configured.
pub fn reserve_size(&self, size_increase: usize) -> Result<(), UserAbort> {
match &self.reserve_size_callback {
Some(callback) => (callback.callback)(size_increase),
None => Ok(()),
}
}
/// Notify the callback about new progress information.
pub fn notify(&self, progress: CompilationProgress) -> Result<(), UserAbort> {
(self.callback)(progress)
}
}
impl fmt::Debug for CompilationProgressCallback {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CompilationProgressCallback").finish()
}
}