mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-04 21:49:40 +00:00
Finished the Pandoc integration (#487)
This commit is contained in:
1 parent
5b685193ef
commit
437da0fd9b
30 files changed
+1600
-349
No files matched your search
@@ -7,7 +7,6 @@ use file_format::{FileFormat, Kind};
|
||||
use futures::{Stream, StreamExt};
|
||||
use pdfium_render::prelude::Pdfium;
|
||||
use tokio::io::AsyncBufReadExt;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
use rocket::Shutdown;
|
||||
@@ -16,6 +15,7 @@ use rocket::tokio::select;
|
||||
use rocket::serde::Serialize;
|
||||
use rocket::get;
|
||||
use crate::api_token::APIToken;
|
||||
use crate::pandoc::PandocProcessBuilder;
|
||||
use crate::pdfium::PdfiumInit;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -255,12 +255,13 @@ async fn convert_with_pandoc(
|
||||
from: &str,
|
||||
to: &str,
|
||||
) -> Result<ChunkStream> {
|
||||
let output = Command::new("pandoc")
|
||||
.arg(file_path)
|
||||
.args(["-f", from, "-t", to])
|
||||
.output()
|
||||
.await?;
|
||||
|
||||
let output = PandocProcessBuilder::new()
|
||||
.with_input_file(file_path)
|
||||
.with_input_format(from)
|
||||
.with_output_format(to)
|
||||
.build()
|
||||
.command.output().await?;
|
||||
|
||||
let stream = stream! {
|
||||
if output.status.success() {
|
||||
match String::from_utf8(output.stdout.clone()) {
|
||||
|
||||
+2
-1
@@ -11,4 +11,5 @@ pub mod runtime_api;
|
||||
pub mod certificate;
|
||||
pub mod file_data;
|
||||
pub mod metadata;
|
||||
pub mod pdfium;
|
||||
pub mod pdfium;
|
||||
pub mod pandoc;
|
||||
@@ -0,0 +1,169 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs;
|
||||
use tokio::process::Command;
|
||||
use crate::environment::DATA_DIRECTORY;
|
||||
use crate::metadata::META_DATA;
|
||||
|
||||
pub struct PandocExecutable {
|
||||
pub executable: String,
|
||||
pub is_local_installation: bool,
|
||||
}
|
||||
|
||||
pub struct PandocPreparedProcess {
|
||||
pub command: Command,
|
||||
pub is_local_installation: bool,
|
||||
}
|
||||
|
||||
pub struct PandocProcessBuilder {
|
||||
provided_input_file: Option<String>,
|
||||
provided_output_file: Option<String>,
|
||||
provided_input_format: Option<String>,
|
||||
provided_output_format: Option<String>,
|
||||
additional_arguments: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for PandocProcessBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PandocProcessBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
provided_input_file: None,
|
||||
provided_output_file: None,
|
||||
provided_input_format: None,
|
||||
provided_output_format: None,
|
||||
additional_arguments: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_input_file<S: Into<String>>(mut self, input_file: S) -> Self {
|
||||
self.provided_input_file = Some(input_file.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_output_file<S: Into<String>>(mut self, output_file: S) -> Self {
|
||||
self.provided_output_file = Some(output_file.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_input_format<S: Into<String>>(mut self, input_format: S) -> Self {
|
||||
self.provided_input_format = Some(input_format.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_output_format<S: Into<String>>(mut self, output_format: S) -> Self {
|
||||
self.provided_output_format = Some(output_format.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_argument<S: Into<String>>(mut self, argument: S) -> Self {
|
||||
self.additional_arguments.push(argument.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> PandocPreparedProcess {
|
||||
let mut arguments = Vec::new();
|
||||
if let Some(input_file) = &self.provided_input_file {
|
||||
arguments.push(input_file.clone());
|
||||
}
|
||||
|
||||
if let Some(input_format) = &self.provided_input_format {
|
||||
arguments.push("-f".to_string());
|
||||
arguments.push(input_format.clone());
|
||||
}
|
||||
|
||||
if let Some(output_format) = &self.provided_output_format {
|
||||
arguments.push("-t".to_string());
|
||||
arguments.push(output_format.clone());
|
||||
}
|
||||
|
||||
for additional_argument in &self.additional_arguments {
|
||||
arguments.push(additional_argument.clone());
|
||||
}
|
||||
|
||||
if let Some(output_file) = &self.provided_output_file {
|
||||
arguments.push("-o".to_string());
|
||||
arguments.push(output_file.clone());
|
||||
}
|
||||
|
||||
let pandoc_executable = Self::pandoc_executable_path();
|
||||
let mut command = Command::new(&pandoc_executable.executable);
|
||||
command.args(&arguments);
|
||||
|
||||
PandocPreparedProcess {
|
||||
command,
|
||||
is_local_installation: pandoc_executable.is_local_installation,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path to the pandoc executable.
|
||||
///
|
||||
/// Any local installation of pandoc will be preferred over the system-wide installation.
|
||||
/// When a local installation is found, its absolute path will be returned. In case no local
|
||||
/// installation is found, the name of the pandoc executable will be returned.
|
||||
fn pandoc_executable_path() -> PandocExecutable {
|
||||
// First, we try to find the pandoc executable in the data directory.
|
||||
// Any local installation should be preferred over the system-wide installation.
|
||||
let data_folder = PathBuf::from(DATA_DIRECTORY.get().unwrap());
|
||||
let local_installation_root_directory = data_folder.join("pandoc");
|
||||
|
||||
if local_installation_root_directory.exists() {
|
||||
let executable_name = Self::pandoc_executable_name();
|
||||
|
||||
if let Ok(entries) = fs::read_dir(&local_installation_root_directory) {
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
if let Ok(pandoc_path) = Self::find_executable_in_dir(&path, &executable_name) {
|
||||
return PandocExecutable {
|
||||
executable: pandoc_path.to_string_lossy().to_string(),
|
||||
is_local_installation: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When no local installation was found, we assume that the pandoc executable is in the system PATH:
|
||||
PandocExecutable {
|
||||
executable: Self::pandoc_executable_name(),
|
||||
is_local_installation: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_executable_in_dir(dir: &Path, executable_name: &str) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
let pandoc_path = dir.join(executable_name);
|
||||
if pandoc_path.exists() && pandoc_path.is_file() {
|
||||
return Ok(pandoc_path);
|
||||
}
|
||||
|
||||
// Recursively search in subdirectories
|
||||
if let Ok(entries) = fs::read_dir(dir) {
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
if let Ok(found_path) = Self::find_executable_in_dir(&path, executable_name) {
|
||||
return Ok(found_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err("Executable not found".into())
|
||||
}
|
||||
|
||||
/// Reads the os platform to determine the used executable name.
|
||||
fn pandoc_executable_name() -> String {
|
||||
let metadata = META_DATA.lock().unwrap();
|
||||
let metadata = metadata.as_ref().unwrap();
|
||||
|
||||
match metadata.architecture.as_str() {
|
||||
"win-arm64" | "win-x64" => "pandoc.exe".to_string(),
|
||||
_ => "pandoc".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user