text_document/
operation.rs1use std::thread;
4use std::time::Duration;
5
6use anyhow::Result;
7
8use frontend::AppContext;
9
10type ResultFn<T> = Box<dyn Fn(&AppContext, &str) -> Option<Result<T>> + Send>;
12
13pub(crate) struct OperationState {
15 ctx: AppContext,
16}
17
18impl OperationState {
19 pub fn new(ctx: &AppContext) -> Self {
20 Self { ctx: ctx.clone() }
21 }
22}
23
24pub struct Operation<T> {
34 id: String,
35 state: OperationState,
36 result_fn: ResultFn<T>,
37}
38
39impl<T> Operation<T> {
40 pub(crate) fn new(id: String, ctx: &AppContext, result_fn: ResultFn<T>) -> Self {
41 Self {
42 id,
43 state: OperationState::new(ctx),
44 result_fn,
45 }
46 }
47
48 pub fn id(&self) -> &str {
50 &self.id
51 }
52
53 pub fn progress(&self) -> Option<(f64, String)> {
56 let mgr = self
57 .state
58 .ctx
59 .long_operation_manager
60 .lock()
61 .ok()
62 .or_else(|| {
63 match self.state.ctx.long_operation_manager.lock() {
65 Ok(g) => Some(g),
66 Err(e) => Some(e.into_inner()),
67 }
68 })?;
69 mgr.get_operation_progress(&self.id)
70 .map(|p| (p.percentage as f64, p.message.unwrap_or_default()))
71 }
72
73 pub fn is_done(&self) -> bool {
75 (self.result_fn)(&self.state.ctx, &self.id).is_some()
76 }
77
78 pub fn cancel(&self) {
80 if let Ok(mgr) = self.state.ctx.long_operation_manager.lock() {
81 mgr.cancel_operation(&self.id);
82 }
83 }
84
85 pub fn wait(self) -> Result<T> {
88 loop {
89 if let Some(result) = (self.result_fn)(&self.state.ctx, &self.id) {
90 return result;
91 }
92 thread::sleep(Duration::from_millis(50));
93 }
94 }
95
96 pub fn wait_timeout(self, timeout: Duration) -> Option<Result<T>> {
99 let deadline = std::time::Instant::now() + timeout;
100 loop {
101 if let Some(result) = (self.result_fn)(&self.state.ctx, &self.id) {
102 return Some(result);
103 }
104 if std::time::Instant::now() >= deadline {
105 return None;
106 }
107 let remaining = deadline.saturating_duration_since(std::time::Instant::now());
108 thread::sleep(remaining.min(Duration::from_millis(50)));
109 }
110 }
111
112 pub fn try_result(&mut self) -> Option<Result<T>> {
115 (self.result_fn)(&self.state.ctx, &self.id)
116 }
117}
118
119#[derive(Debug, Clone)]
123pub struct MarkdownImportResult {
124 pub block_count: usize,
125}
126
127#[derive(Debug, Clone)]
129pub struct HtmlImportResult {
130 pub block_count: usize,
131}
132
133#[derive(Debug, Clone)]
135pub struct DocxExportResult {
136 pub file_path: String,
137 pub paragraph_count: usize,
138}