use std::thread;
use std::time::Duration;
use anyhow::Result;
use frontend::AppContext;
type ResultFn<T> = Box<dyn Fn(&AppContext, &str) -> Option<Result<T>> + Send>;
pub(crate) struct OperationState {
ctx: AppContext,
}
impl OperationState {
pub fn new(ctx: &AppContext) -> Self {
Self { ctx: ctx.clone() }
}
}
pub struct Operation<T> {
id: String,
state: OperationState,
result_fn: ResultFn<T>,
}
impl<T> Operation<T> {
pub(crate) fn new(id: String, ctx: &AppContext, result_fn: ResultFn<T>) -> Self {
Self {
id,
state: OperationState::new(ctx),
result_fn,
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn progress(&self) -> Option<(f64, String)> {
let mgr = self
.state
.ctx
.long_operation_manager
.lock()
.ok()
.or_else(|| {
match self.state.ctx.long_operation_manager.lock() {
Ok(g) => Some(g),
Err(e) => Some(e.into_inner()),
}
})?;
mgr.get_operation_progress(&self.id)
.map(|p| (p.percentage as f64, p.message.unwrap_or_default()))
}
pub fn is_done(&self) -> bool {
(self.result_fn)(&self.state.ctx, &self.id).is_some()
}
pub fn cancel(&self) {
if let Ok(mgr) = self.state.ctx.long_operation_manager.lock() {
mgr.cancel_operation(&self.id);
}
}
pub fn wait(self) -> Result<T> {
loop {
if let Some(result) = (self.result_fn)(&self.state.ctx, &self.id) {
return result;
}
thread::sleep(Duration::from_millis(50));
}
}
pub fn wait_timeout(self, timeout: Duration) -> Option<Result<T>> {
let deadline = std::time::Instant::now() + timeout;
loop {
if let Some(result) = (self.result_fn)(&self.state.ctx, &self.id) {
return Some(result);
}
if std::time::Instant::now() >= deadline {
return None;
}
let remaining = deadline.saturating_duration_since(std::time::Instant::now());
thread::sleep(remaining.min(Duration::from_millis(50)));
}
}
pub fn try_result(&mut self) -> Option<Result<T>> {
(self.result_fn)(&self.state.ctx, &self.id)
}
}
#[derive(Debug, Clone)]
pub struct MarkdownImportResult {
pub block_count: usize,
}
#[derive(Debug, Clone)]
pub struct HtmlImportResult {
pub block_count: usize,
}
#[derive(Debug, Clone)]
pub struct DocxExportResult {
pub file_path: String,
pub paragraph_count: usize,
}