Skip to main content

text_document_io/
document_io_controller.rs

1// Generated by Qleany v1.5.1 from feature_controller.tera
2
3use crate::ExportDjotDto;
4use crate::ExportDocxDto;
5use crate::ExportDocxResultDto;
6use crate::ExportEpubDto;
7use crate::ExportEpubResultDto;
8use crate::ExportHtmlDto;
9use crate::ExportLatexDto;
10use crate::ExportLatexResultDto;
11use crate::ExportMarkdownDto;
12use crate::ExportOdtDto;
13use crate::ExportOdtResultDto;
14use crate::ExportPdfDto;
15use crate::ExportPdfResultDto;
16use crate::ExportPlainTextDto;
17use crate::ImportDjotDto;
18use crate::ImportDjotResultDto;
19use crate::ImportHtmlDto;
20use crate::ImportHtmlResultDto;
21use crate::ImportMarkdownDto;
22use crate::ImportMarkdownResultDto;
23use crate::ImportPlainTextDto;
24use crate::units_of_work::export_djot_uow::ExportDjotUnitOfWorkFactory;
25use crate::units_of_work::export_docx_uow::ExportDocxUnitOfWorkFactory;
26use crate::units_of_work::export_epub_uow::ExportEpubUnitOfWorkFactory;
27use crate::units_of_work::export_html_uow::ExportHtmlUnitOfWorkFactory;
28use crate::units_of_work::export_latex_uow::ExportLatexUnitOfWorkFactory;
29use crate::units_of_work::export_markdown_uow::ExportMarkdownUnitOfWorkFactory;
30use crate::units_of_work::export_odt_uow::ExportOdtUnitOfWorkFactory;
31#[cfg(feature = "pdf")]
32use crate::units_of_work::export_pdf_uow::ExportPdfUnitOfWorkFactory;
33use crate::units_of_work::export_plain_text_uow::ExportPlainTextUnitOfWorkFactory;
34use crate::units_of_work::import_djot_uow::ImportDjotUnitOfWorkFactory;
35use crate::units_of_work::import_html_uow::ImportHtmlUnitOfWorkFactory;
36use crate::units_of_work::import_markdown_uow::ImportMarkdownUnitOfWorkFactory;
37use crate::units_of_work::import_plain_text_uow::ImportPlainTextUnitOfWorkFactory;
38use crate::use_cases::export_djot_uc::ExportDjotUseCase;
39use crate::use_cases::export_docx_uc::ExportDocxUseCase;
40use crate::use_cases::export_epub_uc::ExportEpubUseCase;
41use crate::use_cases::export_html_uc::ExportHtmlUseCase;
42use crate::use_cases::export_latex_uc::ExportLatexUseCase;
43use crate::use_cases::export_markdown_uc::ExportMarkdownUseCase;
44use crate::use_cases::export_odt_uc::ExportOdtUseCase;
45#[cfg(feature = "pdf")]
46use crate::use_cases::export_pdf_uc::ExportPdfUseCase;
47use crate::use_cases::export_plain_text_uc::ExportPlainTextUseCase;
48use crate::use_cases::import_djot_uc::ImportDjotUseCase;
49use crate::use_cases::import_html_uc::ImportHtmlUseCase;
50use crate::use_cases::import_markdown_uc::ImportMarkdownUseCase;
51use crate::use_cases::import_plain_text_uc::ImportPlainTextUseCase;
52use anyhow::Result;
53use common::event::{Event, Origin};
54use common::parser_tools::DjotExportOptions;
55
56use common::event::DocumentIoEvent::ExportDjot;
57use common::event::DocumentIoEvent::ExportHtml;
58use common::event::DocumentIoEvent::ExportLatex;
59use common::event::DocumentIoEvent::ExportMarkdown;
60use common::event::DocumentIoEvent::ExportPlainText;
61use common::event::DocumentIoEvent::ImportDjot;
62use common::event::DocumentIoEvent::ImportPlainText;
63
64use common::long_operation::{LongOperation, LongOperationManager, OperationProgress};
65use common::{database::db_context::DbContext, event::EventHub};
66use std::sync::Arc;
67
68pub fn import_plain_text(
69    db_context: &DbContext,
70    event_hub: &Arc<EventHub>,
71    dto: &ImportPlainTextDto,
72) -> Result<()> {
73    let uow_context = ImportPlainTextUnitOfWorkFactory::new(db_context, event_hub);
74    let mut uc = ImportPlainTextUseCase::new(Box::new(uow_context));
75    uc.execute(dto)?;
76    // Notify that the handling manifest has been loaded
77    event_hub.send_event(Event {
78        origin: Origin::DocumentIo(ImportPlainText),
79        ids: vec![],
80        data: None,
81    });
82    Ok(())
83}
84
85pub fn export_plain_text(
86    db_context: &DbContext,
87    event_hub: &Arc<EventHub>,
88) -> Result<ExportPlainTextDto> {
89    export_plain_text_with(
90        db_context,
91        event_hub,
92        common::parser_tools::PlainTextExportOptions::addressable(),
93    )
94}
95
96/// [`export_plain_text`], with the presentation options a written-out `.txt` file wants.
97///
98/// Separate entry point rather than flags on the default one, because the default one's
99/// output is load-bearing: it is pinned to the document's addressable text, so anything
100/// that indents or inserts would move every search offset after it.
101pub fn export_plain_text_with(
102    db_context: &DbContext,
103    event_hub: &Arc<EventHub>,
104    options: common::parser_tools::PlainTextExportOptions,
105) -> Result<ExportPlainTextDto> {
106    let uow_context = ExportPlainTextUnitOfWorkFactory::new(db_context);
107    let mut uc = ExportPlainTextUseCase::new(Box::new(uow_context));
108    let return_dto = uc.execute(options)?;
109    // Notify that the handling manifest has been loaded
110    event_hub.send_event(Event {
111        origin: Origin::DocumentIo(ExportPlainText),
112        ids: vec![],
113        data: None,
114    });
115    Ok(return_dto)
116}
117
118pub fn import_markdown(
119    db_context: &DbContext,
120    event_hub: &Arc<EventHub>,
121    long_operation_manager: &mut LongOperationManager,
122    dto: &ImportMarkdownDto,
123) -> Result<String> {
124    let uow_context = ImportMarkdownUnitOfWorkFactory::new(db_context, event_hub);
125    let uc = ImportMarkdownUseCase::new(Box::new(uow_context), dto);
126    let operation_id = long_operation_manager.start_operation(uc);
127    Ok(operation_id)
128}
129
130pub fn get_import_markdown_progress(
131    long_operation_manager: &LongOperationManager,
132    operation_id: &str,
133) -> Option<OperationProgress> {
134    long_operation_manager.get_operation_progress(operation_id)
135}
136
137pub fn get_import_markdown_result(
138    long_operation_manager: &LongOperationManager,
139    operation_id: &str,
140) -> Result<Option<ImportMarkdownResultDto>> {
141    // Get the operation result as a JSON string
142    let result_json = long_operation_manager.get_operation_result(operation_id);
143
144    // If there's no result, return None
145    if result_json.is_none() {
146        return Ok(None);
147    }
148    // Parse the JSON string into a ImportMarkdownResultDto
149    let result_dto: ImportMarkdownResultDto = serde_json::from_str(&result_json.unwrap())?;
150
151    Ok(Some(result_dto))
152}
153
154pub fn export_markdown(
155    db_context: &DbContext,
156    event_hub: &Arc<EventHub>,
157) -> Result<ExportMarkdownDto> {
158    export_markdown_with(
159        db_context,
160        event_hub,
161        common::parser_tools::MarkdownExportOptions::default(),
162    )
163}
164
165/// [`export_markdown`] with the presentation opt-ins. Separate for the same reason as
166/// its plain-text sibling: the default output stays plain Markdown, with no raw HTML in
167/// it that nobody asked for.
168pub fn export_markdown_with(
169    db_context: &DbContext,
170    event_hub: &Arc<EventHub>,
171    options: common::parser_tools::MarkdownExportOptions,
172) -> Result<ExportMarkdownDto> {
173    let uow_context = ExportMarkdownUnitOfWorkFactory::new(db_context);
174    let mut uc = ExportMarkdownUseCase::new(Box::new(uow_context), options);
175    let return_dto = uc.execute()?;
176    // Notify that the handling manifest has been loaded
177    event_hub.send_event(Event {
178        origin: Origin::DocumentIo(ExportMarkdown),
179        ids: vec![],
180        data: None,
181    });
182    Ok(return_dto)
183}
184
185pub fn import_djot(
186    db_context: &DbContext,
187    event_hub: &Arc<EventHub>,
188    long_operation_manager: &mut LongOperationManager,
189    dto: &ImportDjotDto,
190) -> Result<String> {
191    let uow_context = ImportDjotUnitOfWorkFactory::new(db_context, event_hub);
192    let uc = ImportDjotUseCase::new(Box::new(uow_context), dto);
193    let operation_id = long_operation_manager.start_operation(uc);
194    Ok(operation_id)
195}
196
197/// Import Djot **synchronously**, on the calling thread.
198///
199/// [`import_djot`] hands the use case to the `LongOperationManager`, which spawns a
200/// thread for it. That is right for a UI import (it must not block the frame loop),
201/// and wrong for a *batch* caller — a backend that has to parse and rewrite hundreds
202/// of documents in one operation would spawn hundreds of threads to do work it wants
203/// to do inline anyway.
204///
205/// The use case itself is thread-agnostic: it only needs a progress sink and a cancel
206/// flag. So this hands it a no-op sink and a never-set flag and calls it directly. No
207/// thread, no operation id, no polling — the result is simply returned.
208///
209/// (Cancellation is the caller's business here: a synchronous caller can just stop
210/// calling. The flag exists to satisfy the `LongOperation` signature, not to be used.)
211pub fn import_djot_sync(
212    db_context: &DbContext,
213    event_hub: &Arc<EventHub>,
214    dto: &ImportDjotDto,
215) -> Result<ImportDjotResultDto> {
216    let uow_context = ImportDjotUnitOfWorkFactory::new(db_context, event_hub);
217    let uc = ImportDjotUseCase::new(Box::new(uow_context), dto);
218    let result = uc.execute(
219        Box::new(|_progress| {}),
220        Arc::new(std::sync::atomic::AtomicBool::new(false)),
221    )?;
222    event_hub.send_event(Event {
223        origin: Origin::DocumentIo(ImportDjot),
224        ids: vec![],
225        data: None,
226    });
227    Ok(result)
228}
229
230pub fn get_import_djot_progress(
231    long_operation_manager: &LongOperationManager,
232    operation_id: &str,
233) -> Option<OperationProgress> {
234    long_operation_manager.get_operation_progress(operation_id)
235}
236
237pub fn get_import_djot_result(
238    long_operation_manager: &LongOperationManager,
239    operation_id: &str,
240) -> Result<Option<ImportDjotResultDto>> {
241    let result_json = long_operation_manager.get_operation_result(operation_id);
242    if result_json.is_none() {
243        return Ok(None);
244    }
245    let result_dto: ImportDjotResultDto = serde_json::from_str(&result_json.unwrap())?;
246    Ok(Some(result_dto))
247}
248
249pub fn export_djot(
250    db_context: &DbContext,
251    event_hub: &Arc<EventHub>,
252    options: &DjotExportOptions,
253) -> Result<ExportDjotDto> {
254    let uow_context = ExportDjotUnitOfWorkFactory::new(db_context);
255    let mut uc = ExportDjotUseCase::new(Box::new(uow_context));
256    let return_dto = uc.execute(options)?;
257    event_hub.send_event(Event {
258        origin: Origin::DocumentIo(ExportDjot),
259        ids: vec![],
260        data: None,
261    });
262    Ok(return_dto)
263}
264
265pub fn import_html(
266    db_context: &DbContext,
267    event_hub: &Arc<EventHub>,
268    long_operation_manager: &mut LongOperationManager,
269    dto: &ImportHtmlDto,
270) -> Result<String> {
271    let uow_context = ImportHtmlUnitOfWorkFactory::new(db_context, event_hub);
272    let uc = ImportHtmlUseCase::new(Box::new(uow_context), dto);
273    let operation_id = long_operation_manager.start_operation(uc);
274    Ok(operation_id)
275}
276
277pub fn get_import_html_progress(
278    long_operation_manager: &LongOperationManager,
279    operation_id: &str,
280) -> Option<OperationProgress> {
281    long_operation_manager.get_operation_progress(operation_id)
282}
283
284pub fn get_import_html_result(
285    long_operation_manager: &LongOperationManager,
286    operation_id: &str,
287) -> Result<Option<ImportHtmlResultDto>> {
288    // Get the operation result as a JSON string
289    let result_json = long_operation_manager.get_operation_result(operation_id);
290
291    // If there's no result, return None
292    if result_json.is_none() {
293        return Ok(None);
294    }
295    // Parse the JSON string into a ImportHtmlResultDto
296    let result_dto: ImportHtmlResultDto = serde_json::from_str(&result_json.unwrap())?;
297
298    Ok(Some(result_dto))
299}
300
301pub fn export_html(db_context: &DbContext, event_hub: &Arc<EventHub>) -> Result<ExportHtmlDto> {
302    export_html_with_options(db_context, event_hub, Default::default())
303}
304
305/// HTML export with an explicit image policy (reference / data URI / omit).
306pub fn export_html_with_options(
307    db_context: &DbContext,
308    event_hub: &Arc<EventHub>,
309    options: common::parser_tools::HtmlExportOptions,
310) -> Result<ExportHtmlDto> {
311    let uow_context = ExportHtmlUnitOfWorkFactory::new(db_context);
312    let mut uc = ExportHtmlUseCase::new(Box::new(uow_context), options);
313    let return_dto = uc.execute()?;
314    // Notify that the handling manifest has been loaded
315    event_hub.send_event(Event {
316        origin: Origin::DocumentIo(ExportHtml),
317        ids: vec![],
318        data: None,
319    });
320    Ok(return_dto)
321}
322
323pub fn export_latex(
324    db_context: &DbContext,
325    event_hub: &Arc<EventHub>,
326    dto: &ExportLatexDto,
327) -> Result<ExportLatexResultDto> {
328    let uow_context = ExportLatexUnitOfWorkFactory::new(db_context);
329    let mut uc = ExportLatexUseCase::new(Box::new(uow_context));
330    let return_dto = uc.execute(dto)?;
331    // Notify that the handling manifest has been loaded
332    event_hub.send_event(Event {
333        origin: Origin::DocumentIo(ExportLatex),
334        ids: vec![],
335        data: None,
336    });
337    Ok(return_dto)
338}
339
340pub fn export_docx(
341    db_context: &DbContext,
342    _event_hub: &Arc<EventHub>,
343    long_operation_manager: &mut LongOperationManager,
344    dto: &ExportDocxDto,
345) -> Result<String> {
346    let uow_context = ExportDocxUnitOfWorkFactory::new(db_context);
347    let uc = ExportDocxUseCase::new(Box::new(uow_context), dto);
348    let operation_id = long_operation_manager.start_operation(uc);
349    Ok(operation_id)
350}
351
352/// Build the in-memory DOCX document for `db_context` without writing a file.
353///
354/// This runs the exact same builder used by [`export_docx`] but returns the
355/// assembled [`docx_rs::Docx`] instead of packing it to disk, so callers can
356/// inspect the produced structure. Intended for tests; the type is re-exported
357/// as [`crate::docx_rs`] so callers can name it.
358#[doc(hidden)]
359pub fn build_docx_document(db_context: &DbContext, dto: &ExportDocxDto) -> Result<docx_rs::Docx> {
360    let uow_context = ExportDocxUnitOfWorkFactory::new(db_context);
361    let uc = ExportDocxUseCase::new(Box::new(uow_context), dto);
362    let (docx, _paragraph_count) = uc.build_document()?;
363    Ok(docx)
364}
365
366/// As [`build_docx_document`], but also runs the post-`build()` raw-XML comment patch
367/// (`w15:done`, `w:initials`, the uid attribute) and returns the packable
368/// [`docx_rs::XMLDocx`] instead of the pre-`build()` [`docx_rs::Docx`]. Intended for tests
369/// asserting on those three fields, none of which the bare builder struct exposes — they only
370/// exist in the packed XML bytes. Also used to produce a real, on-disk `.docx` for the
371/// golden-fixture LibreOffice conversion test, since `docx_rs::Docx::build().pack(..)` alone
372/// would skip the patch entirely.
373#[doc(hidden)]
374pub fn build_docx_xml_document(
375    db_context: &DbContext,
376    dto: &ExportDocxDto,
377) -> Result<docx_rs::XMLDocx> {
378    let uow_context = ExportDocxUnitOfWorkFactory::new(db_context);
379    let uc = ExportDocxUseCase::new(Box::new(uow_context), dto);
380    uc.build_document_xml()
381}
382
383pub fn get_export_docx_progress(
384    long_operation_manager: &LongOperationManager,
385    operation_id: &str,
386) -> Option<OperationProgress> {
387    long_operation_manager.get_operation_progress(operation_id)
388}
389
390pub fn get_export_docx_result(
391    long_operation_manager: &LongOperationManager,
392    operation_id: &str,
393) -> Result<Option<ExportDocxResultDto>> {
394    // Get the operation result as a JSON string
395    let result_json = long_operation_manager.get_operation_result(operation_id);
396
397    // If there's no result, return None
398    if result_json.is_none() {
399        return Ok(None);
400    }
401    // Parse the JSON string into a ExportDocxResultDto
402    let result_dto: ExportDocxResultDto = serde_json::from_str(&result_json.unwrap())?;
403
404    Ok(Some(result_dto))
405}
406
407pub fn export_epub(
408    db_context: &DbContext,
409    _event_hub: &Arc<EventHub>,
410    long_operation_manager: &mut LongOperationManager,
411    dto: &ExportEpubDto,
412) -> Result<String> {
413    let uow_context = ExportEpubUnitOfWorkFactory::new(db_context);
414    let uc = ExportEpubUseCase::new(Box::new(uow_context), dto);
415    let operation_id = long_operation_manager.start_operation(uc);
416    Ok(operation_id)
417}
418
419/// Build the packaged EPUB bytes for `db_context` without writing a file.
420///
421/// This runs the exact same builder used by [`export_epub`] but returns the assembled `.epub`
422/// zip archive as bytes instead of writing it to disk, so callers (tests, notably) can inspect
423/// the produced package directly — e.g. with the `zip` crate.
424#[doc(hidden)]
425pub fn build_epub_document(db_context: &DbContext, dto: &ExportEpubDto) -> Result<Vec<u8>> {
426    let uow_context = ExportEpubUnitOfWorkFactory::new(db_context);
427    let uc = ExportEpubUseCase::new(Box::new(uow_context), dto);
428    let (epub_bytes, _chapter_count) = uc.build_document()?;
429    Ok(epub_bytes)
430}
431
432pub fn get_export_epub_progress(
433    long_operation_manager: &LongOperationManager,
434    operation_id: &str,
435) -> Option<OperationProgress> {
436    long_operation_manager.get_operation_progress(operation_id)
437}
438
439pub fn get_export_epub_result(
440    long_operation_manager: &LongOperationManager,
441    operation_id: &str,
442) -> Result<Option<ExportEpubResultDto>> {
443    // Get the operation result as a JSON string
444    let result_json = long_operation_manager.get_operation_result(operation_id);
445
446    // If there's no result, return None
447    if result_json.is_none() {
448        return Ok(None);
449    }
450    // Parse the JSON string into a ExportEpubResultDto
451    let result_dto: ExportEpubResultDto = serde_json::from_str(&result_json.unwrap())?;
452
453    Ok(Some(result_dto))
454}
455
456pub fn export_odt(
457    db_context: &DbContext,
458    _event_hub: &Arc<EventHub>,
459    long_operation_manager: &mut LongOperationManager,
460    dto: &ExportOdtDto,
461) -> Result<String> {
462    let uow_context = ExportOdtUnitOfWorkFactory::new(db_context);
463    let uc = ExportOdtUseCase::new(Box::new(uow_context), dto);
464    let operation_id = long_operation_manager.start_operation(uc);
465    Ok(operation_id)
466}
467
468/// Build the packaged ODT bytes for `db_context` without writing a file.
469///
470/// This runs the exact same builder used by [`export_odt`] but returns the assembled `.odt` zip
471/// archive as bytes instead of writing it to disk, so callers (tests, notably) can inspect the
472/// produced package directly — the ODT analog of [`build_epub_document`].
473#[doc(hidden)]
474pub fn build_odt_document(db_context: &DbContext, dto: &ExportOdtDto) -> Result<Vec<u8>> {
475    let uow_context = ExportOdtUnitOfWorkFactory::new(db_context);
476    let uc = ExportOdtUseCase::new(Box::new(uow_context), dto);
477    let (odt_bytes, _paragraph_count) = uc.build_document()?;
478    Ok(odt_bytes)
479}
480
481pub fn get_export_odt_progress(
482    long_operation_manager: &LongOperationManager,
483    operation_id: &str,
484) -> Option<OperationProgress> {
485    long_operation_manager.get_operation_progress(operation_id)
486}
487
488pub fn get_export_odt_result(
489    long_operation_manager: &LongOperationManager,
490    operation_id: &str,
491) -> Result<Option<ExportOdtResultDto>> {
492    let result_json = long_operation_manager.get_operation_result(operation_id);
493    if result_json.is_none() {
494        return Ok(None);
495    }
496    let result_dto: ExportOdtResultDto = serde_json::from_str(&result_json.unwrap())?;
497    Ok(Some(result_dto))
498}
499
500// ─── PDF export (via embedded Typst) — dual-cfg ────────────────────────────
501//
502// `export_pdf`/`get_export_pdf_progress`/`get_export_pdf_result` exist with the SAME signature
503// regardless of whether the `pdf` cargo feature is enabled on `text-document-io`. This is what
504// lets `frontend`'s command wrappers and `public_api`'s `to_pdf`/`to_pdf_with_options` call these
505// symbols unconditionally (no `#[cfg]` in that consumer code) — the feature only decides which
506// body is compiled: a real `LongOperation`-backed export, or an immediate "unsupported" error.
507// Cargo feature unification means any crate in the build graph enabling `pdf` turns it on
508// globally for that build, so a stable always-present API surface that fails at *runtime* is
509// strictly better here than two divergent source trees.
510
511#[cfg(feature = "pdf")]
512pub fn export_pdf(
513    db_context: &DbContext,
514    _event_hub: &Arc<EventHub>,
515    long_operation_manager: &mut LongOperationManager,
516    dto: &ExportPdfDto,
517) -> Result<String> {
518    let uow_context = ExportPdfUnitOfWorkFactory::new(db_context);
519    let uc = ExportPdfUseCase::new(Box::new(uow_context), dto);
520    let operation_id = long_operation_manager.start_operation(uc);
521    Ok(operation_id)
522}
523
524#[cfg(not(feature = "pdf"))]
525pub fn export_pdf(
526    _db_context: &DbContext,
527    _event_hub: &Arc<EventHub>,
528    _long_operation_manager: &mut LongOperationManager,
529    _dto: &ExportPdfDto,
530) -> Result<String> {
531    Err(anyhow::anyhow!(
532        "PDF export requires the `pdf` cargo feature on the `text-document-io` crate"
533    ))
534}
535
536/// Build the PDF bytes for `db_context` without writing a file.
537///
538/// This runs the exact same builder used by [`export_pdf`] but returns the compiled PDF bytes
539/// instead of writing them to disk, so callers (tests, notably) can inspect the produced document
540/// directly.
541#[cfg(feature = "pdf")]
542#[doc(hidden)]
543pub fn build_pdf_document(db_context: &DbContext, dto: &ExportPdfDto) -> Result<Vec<u8>> {
544    let uow_context = ExportPdfUnitOfWorkFactory::new(db_context);
545    let uc = ExportPdfUseCase::new(Box::new(uow_context), dto);
546    let (pdf_bytes, _page_count) = uc.build_document()?;
547    Ok(pdf_bytes)
548}
549
550#[cfg(feature = "pdf")]
551pub fn get_export_pdf_progress(
552    long_operation_manager: &LongOperationManager,
553    operation_id: &str,
554) -> Option<OperationProgress> {
555    long_operation_manager.get_operation_progress(operation_id)
556}
557
558#[cfg(not(feature = "pdf"))]
559pub fn get_export_pdf_progress(
560    _long_operation_manager: &LongOperationManager,
561    _operation_id: &str,
562) -> Option<OperationProgress> {
563    None
564}
565
566#[cfg(feature = "pdf")]
567pub fn get_export_pdf_result(
568    long_operation_manager: &LongOperationManager,
569    operation_id: &str,
570) -> Result<Option<ExportPdfResultDto>> {
571    // Get the operation result as a JSON string
572    let result_json = long_operation_manager.get_operation_result(operation_id);
573
574    // If there's no result, return None
575    if result_json.is_none() {
576        return Ok(None);
577    }
578    // Parse the JSON string into a ExportPdfResultDto
579    let result_dto: ExportPdfResultDto = serde_json::from_str(&result_json.unwrap())?;
580
581    Ok(Some(result_dto))
582}
583
584#[cfg(not(feature = "pdf"))]
585pub fn get_export_pdf_result(
586    _long_operation_manager: &LongOperationManager,
587    _operation_id: &str,
588) -> Result<Option<ExportPdfResultDto>> {
589    Ok(None)
590}