Skip to main content

cloudiful_docling_convert/processor/service/
mod.rs

1use crate::api::DoclingClient;
2use crate::document::{
3    ConvertOptions, ConvertRequest, ConvertedDocument, ConvertedFile, FileConvertRequest, InputKind,
4};
5use crate::error::{PdfConvertError, Result};
6use std::future::Future;
7
8mod output;
9mod pdf;
10mod text;
11
12#[cfg(test)]
13mod tests;
14
15pub struct DocumentConverter {
16    docling_client: DoclingClient,
17}
18
19impl DocumentConverter {
20    pub fn new(docling_client: DoclingClient) -> Self {
21        Self { docling_client }
22    }
23
24    pub async fn convert(&self, request: ConvertRequest) -> Result<ConvertedDocument> {
25        self.convert_with_progress(request, |_, _| async {}).await
26    }
27
28    pub async fn convert_with_progress<F, Fut>(
29        &self,
30        request: ConvertRequest,
31        mut on_progress: F,
32    ) -> Result<ConvertedDocument>
33    where
34        F: FnMut(usize, usize) -> Fut + Send,
35        Fut: Future<Output = ()> + Send,
36    {
37        let input_kind = request.validate()?;
38
39        match (&request.options, input_kind) {
40            (ConvertOptions::Text(options), InputKind::Text) => {
41                let document =
42                    self.convert_text(&request.input, options, &request.output_formats)?;
43                on_progress(1, 1).await;
44                Ok(document)
45            }
46            (ConvertOptions::Generic(options), InputKind::Docx | InputKind::Markdown) => {
47                let document = self
48                    .convert_generic(&request.input, options, &request.output_formats)
49                    .await?;
50                on_progress(1, 1).await;
51                Ok(document)
52            }
53            (ConvertOptions::Pdf(options), InputKind::Pdf) => {
54                self.convert_pdf(
55                    &request.input,
56                    options,
57                    &request.output_formats,
58                    &mut on_progress,
59                )
60                .await
61            }
62            _ => Err(PdfConvertError::validation_error(
63                "request",
64                "input kind and convert options do not match",
65            )),
66        }
67    }
68
69    pub async fn convert_to_file(&self, request: FileConvertRequest) -> Result<ConvertedFile> {
70        self.convert_to_file_with_progress(request, |_, _| async {})
71            .await
72    }
73
74    pub async fn convert_to_file_with_progress<F, Fut>(
75        &self,
76        request: FileConvertRequest,
77        on_progress: F,
78    ) -> Result<ConvertedFile>
79    where
80        F: FnMut(usize, usize) -> Fut + Send,
81        Fut: Future<Output = ()> + Send,
82    {
83        let document = self
84            .convert_with_progress(request.request.clone(), on_progress)
85            .await?;
86        let output_path = Self::calculate_output_path(
87            &request.output_dir,
88            &document.filename,
89            request.selected_output,
90        );
91
92        if !request.overwrite && output_path.exists() {
93            return Err(PdfConvertError::operation_error(
94                "writing output",
95                format!(
96                    "output already exists and overwrite is disabled: {}",
97                    output_path.display()
98                ),
99            ));
100        }
101
102        Self::write_output_file(&output_path, &document, request.selected_output).await?;
103
104        Ok(ConvertedFile {
105            document,
106            output_paths: vec![output_path],
107        })
108    }
109}