Files
concept-maker/src-tauri/src/lib.rs

141 lines
4.1 KiB
Rust

use serde_json::Value;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use tauri::{
menu::{Menu, MenuItem, Submenu},
Emitter,
};
fn repo_root() -> PathBuf {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
manifest_dir.parent().unwrap_or(&manifest_dir).to_path_buf()
}
fn run_python(script: &str, input: &str) -> Result<Vec<u8>, String> {
let cwd = repo_root();
let mut last_err: Option<String> = None;
for candidate in ["python3", "python"] {
let mut child = match Command::new(candidate)
.arg(script)
.current_dir(&cwd)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
{
Ok(child) => child,
Err(err) => {
last_err = Some(format!("{candidate}: {err}"));
continue;
}
};
if let Some(mut stdin) = child.stdin.take() {
if let Err(err) = stdin.write_all(input.as_bytes()) {
return Err(format!("Failed to write to Python stdin: {err}"));
}
}
let output = match child.wait_with_output() {
Ok(output) => output,
Err(err) => {
last_err = Some(format!("{candidate}: {err}"));
continue;
}
};
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("Python backend failed: {stderr}"));
}
return Ok(output.stdout);
}
Err(last_err.unwrap_or_else(|| "Python not found".to_string()))
}
#[tauri::command]
fn run_python_action(action: String, payload: Value) -> Result<Value, String> {
let script = repo_root().join("concept_api.py");
if !script.exists() {
return Err("concept_api.py not found".to_string());
}
let req = serde_json::json!({
"action": action,
"payload": payload,
});
let input = serde_json::to_string(&req).map_err(|e| e.to_string())?;
let stdout = run_python(script.to_string_lossy().as_ref(), &input)?;
let resp: Value = serde_json::from_slice(&stdout).map_err(|e| e.to_string())?;
if resp.get("ok") == Some(&Value::Bool(true)) {
Ok(resp.get("data").cloned().unwrap_or(Value::Null))
} else {
let error = resp
.get("error")
.and_then(|v| v.as_str())
.unwrap_or("Unknown backend error");
Err(error.to_string())
}
}
#[tauri::command]
fn open_path(path: String) -> Result<(), String> {
let path = PathBuf::from(path);
if !path.exists() {
return Err(format!("Path does not exist: {}", path.display()));
}
let mut command = if cfg!(target_os = "macos") {
let mut command = Command::new("open");
command.arg(&path);
command
} else if cfg!(target_os = "windows") {
let mut command = Command::new("cmd");
command.arg("/C").arg("start").arg("").arg(&path);
command
} else {
let mut command = Command::new("xdg-open");
command.arg(&path);
command
};
command
.spawn()
.map(|_| ())
.map_err(|err| format!("Failed to open {}: {err}", path.display()))
}
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.menu(|handle| {
let new_item = MenuItem::with_id(handle, "file-new", "New", true, None::<&str>)?;
let open_item = MenuItem::with_id(handle, "file-open", "Open", true, None::<&str>)?;
let save_item = MenuItem::with_id(handle, "file-save", "Save", true, None::<&str>)?;
let file_menu = Submenu::with_items(
handle,
"File",
true,
&[&new_item, &open_item, &save_item],
)?;
let open_settings =
MenuItem::with_id(handle, "settings-open", "Open Settings", true, None::<&str>)?;
let settings_menu = Submenu::with_items(handle, "Settings", true, &[&open_settings])?;
Menu::with_items(handle, &[&file_menu, &settings_menu])
})
.on_menu_event(|app, event| match event.id().as_ref() {
"file-new" | "file-open" | "file-save" | "settings-open" => {
let _ = app.emit("app-menu-action", event.id().as_ref());
}
_ => {}
})
.invoke_handler(tauri::generate_handler![run_python_action, open_path])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}