Skip to content

Instantly share code, notes, and snippets.

@samuelint
Created June 27, 2024 17:31
Show Gist options
  • Save samuelint/6f7c55f0d86f9968c051b4fdd470a060 to your computer and use it in GitHub Desktop.
Save samuelint/6f7c55f0d86f9968c051b4fdd470a060 to your computer and use it in GitHub Desktop.
Tauri Sidecar Lifecycle & Forward stderr
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
pub mod sidecar_lifecycle_service;
use std::sync::Mutex;
use tauri_plugin_log::LogTarget;
use tauri::{Manager, State, WindowEvent};
use sidecar_lifecycle_service::SidecarLifeCycleService;
struct AppState {
code_sidecar_mutex: Mutex<SidecarLifeCycleService>,
}
#[tauri::command]
fn start_server(api_manager_state: State<AppState>) -> Result<String, String> {
let am = api_manager_state
.code_sidecar_mutex
.lock()
.unwrap()
.start();
am
}
#[tauri::command]
fn stop_server(api_manager_state: State<AppState>) -> Result<String, String> {
let app_state = api_manager_state
.code_sidecar_mutex
.lock()
.unwrap()
.stop();
app_state
}
fn main() {
let core_sidecar = SidecarLifeCycleService::new("core");
let state = AppState {
code_sidecar_mutex: Mutex::new(core_sidecar),
};
let log_builder = tauri_plugin_log::Builder::default().targets([
LogTarget::LogDir,
LogTarget::Stdout,
LogTarget::Stderr,
LogTarget::Webview,
]);
tauri::Builder::default()
.manage(state)
.plugin(log_builder.build())
.setup(move |app| {
let app_state: State<AppState> = app.state();
app_state.code_sidecar_mutex
.lock()
.unwrap()
.start()
.expect("Core Sidecar start failed");
Ok(())
})
.on_window_event(move |event| match event.event() {
WindowEvent::Destroyed => {
let am: State<AppState> = event.window().state();
am.code_sidecar_mutex
.lock()
.unwrap()
.stop()
.expect("Core Sidecar stop failed");
}
_ => {}
})
.invoke_handler(tauri::generate_handler![
start_server,
stop_server,
])
.run(tauri::generate_context!())
.expect("[Error] while running tauri application");
}
use std::borrow::BorrowMut;
use std::process::{Child, Command, Stdio};
use command_group::{Signal, UnixChildExt};
use tauri::api::process::Command as TCommand;
use log::{info, error};
use std::io::{BufReader, BufRead};
use std::thread;
fn log_child_stderr(mut child: Child) -> Child {
if let Some(stderr) = child.stderr.take() {
let stderr_reader = BufReader::new(stderr);
thread::spawn(move || {
for line in stderr_reader.lines() {
match line {
Ok(line) => error!("[sidecar | core]: {}", line.trim_end_matches('\n')),
Err(err) => error!("Error reading stderr: {}", err),
}
}
});
}
child
}
pub struct SidecarLifeCycleService {
program: String,
sidecar_command: Command,
child: Option<Child>,
}
impl SidecarLifeCycleService {
pub fn new<S: Into<String>>(program: S) -> SidecarLifeCycleService {
let program_string = program.into();
let sidecar_command = TCommand::new_sidecar(&program_string).expect("failed to setup sidecar");
SidecarLifeCycleService {
program: program_string,
sidecar_command: sidecar_command.into(),
child: None,
}
}
pub fn start(&mut self) -> Result<String, String> {
match self.child.borrow_mut() {
Some(_) => {
let info = format!("Sidecar {} already running", self.program);
info!("{}", &info);
Ok(info.into())
}
None => {
let child = self.sidecar_command.stderr(Stdio::piped()).spawn();
match child {
Ok(mut child) => {
let id = child.id();
child = log_child_stderr(child);
self.child = Some(child);
let info = format!("Sidecar {} started - {}", self.program, id);
info!("{}", &info);
Ok(info.into())
}
Err(e) => {
let info = format!("Sidecar {} start failed - {}", self.program, e.to_string());
error!("{}", &info);
Err(info.into())
}
}
}
}
}
pub fn stop(&mut self) -> Result<String, String> {
match self.child.borrow_mut() {
Some(child) => {
let id = child.id();
child
.signal(Signal::SIGTERM)
.expect("Some error happened when killing child process");
self.child = None;
let info = format!("Sidecar {} stopped - {}", self.program, id);
info!("{}", &info);
Ok(info.into())
}
_ => {
let info = format!("Sidecar {} stop failed", self.program);
println!("{}", &info);
Ok(info.into())
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment