refactor 4

This commit is contained in:
Julian Freeman
2026-04-18 16:05:00 -04:00
parent 0fc523e234
commit 2625c8b52f
4 changed files with 273 additions and 71 deletions

View File

@@ -64,6 +64,18 @@ pub struct ResolvedPostInstall {
pub steps: Vec<PostInstallStep>,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct TaskEventPayload {
pub task_id: String,
pub software_id: String,
pub task_type: String,
pub status: String,
pub stage: String,
pub progress: f32,
pub target_version: Option<String>,
pub message: Option<String>,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct EssentialsStatusItem {
pub id: String,

View File

@@ -11,17 +11,19 @@ use tokio::sync::mpsc;
use winreg::enums::*;
use winreg::RegKey;
use crate::domain::models::{InstallProgress, InstallTask};
use crate::domain::models::{InstallProgress, InstallTask, TaskEventPayload};
use crate::services::essentials_service;
use crate::services::log_service::emit_log;
use crate::winget::PostInstallStep;
pub struct AppState {
pub install_tx: mpsc::Sender<InstallTask>,
pub app_handle: AppHandle,
}
pub fn create_install_state(handle: AppHandle) -> AppState {
let (tx, mut rx) = mpsc::channel::<InstallTask>(100);
let runtime_handle = handle.clone();
tauri::async_runtime::spawn(async move {
let perc_re = Regex::new(r"(\d+)\s*%").unwrap();
@@ -35,14 +37,16 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
let enable_post_install_flag = task.enable_post_install;
let log_id = format!("install-{}", task_id);
let _ = handle.emit(
"install-status",
InstallProgress {
id: task_id.clone(),
status: "installing".to_string(),
progress: 0.0,
},
emit_task_event(
&runtime_handle,
&log_id,
&task_id,
"install",
"running",
"installing",
0.0,
task_version.clone(),
None,
);
let mut args = vec!["install".to_string()];
@@ -52,8 +56,19 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
if use_manifest && manifest_url.is_some() {
let url = manifest_url.unwrap();
display_cmd = format!("Winget Install (Manifest): {} from {}", task_id, url);
emit_task_event(
&runtime_handle,
&log_id,
&task_id,
"install",
"running",
"downloading_manifest",
0.0,
task_version.clone(),
Some("Downloading remote manifest".to_string()),
);
emit_log(
&handle,
&runtime_handle,
&log_id,
&display_cmd,
"Downloading remote manifest...",
@@ -85,19 +100,22 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
if temp_manifest_path.is_none() {
emit_log(
&handle,
&runtime_handle,
&log_id,
"Error",
"Failed to download or save manifest.",
"error",
);
let _ = handle.emit(
"install-status",
InstallProgress {
id: task_id.clone(),
status: "error".to_string(),
progress: 0.0,
},
emit_task_event(
&runtime_handle,
&log_id,
&task_id,
"install",
"failed",
"manifest_error",
0.0,
task_version.clone(),
Some("Failed to download or save manifest".to_string()),
);
continue;
}
@@ -128,12 +146,23 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
let full_command = format!("winget {}", args.join(" "));
emit_log(
&handle,
&runtime_handle,
&log_id,
&display_cmd,
&format!("Executing: {}\n---", full_command),
"info",
);
emit_task_event(
&runtime_handle,
&log_id,
&task_id,
"install",
"running",
"invoking_winget",
0.0,
task_version.clone(),
None,
);
let child = Command::new("winget")
.args(&args)
@@ -147,7 +176,7 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
let stdout_handle = child_proc.stdout.take().map(|stdout| {
spawn_install_stream_reader(
stdout,
handle.clone(),
runtime_handle.clone(),
log_id.clone(),
task_id.clone(),
"stdout",
@@ -158,7 +187,7 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
let stderr_handle = child_proc.stderr.take().map(|stderr| {
spawn_install_stream_reader(
stderr,
handle.clone(),
runtime_handle.clone(),
log_id.clone(),
task_id.clone(),
"stderr",
@@ -177,7 +206,7 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
let status_result = if exit_status { "success" } else { "error" };
if status_result == "success" && enable_post_install_flag {
let software_info = essentials_service::get_essentials(&handle)
let software_info = essentials_service::get_essentials(&runtime_handle)
.and_then(|repo| repo.essentials.into_iter().find(|s| s.id == task_id));
if let Some(sw) = software_info {
@@ -188,7 +217,7 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
}
} else if let Some(url) = sw.post_install_url {
emit_log(
&handle,
&runtime_handle,
&log_id,
"Post-Install",
"Local config not found, fetching remote config...",
@@ -206,7 +235,7 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
match serde_json::from_str::<Vec<PostInstallStep>>(&text) {
Ok(steps) => {
emit_log(
&handle,
&runtime_handle,
&log_id,
"Post-Install",
&format!(
@@ -219,7 +248,7 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
}
Err(e) => {
emit_log(
&handle,
&runtime_handle,
&log_id,
"Post-Install Error",
&format!("JSON Parse Error: {}. Raw Content: {}", e, text),
@@ -230,7 +259,7 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
}
} else {
emit_log(
&handle,
&runtime_handle,
&log_id,
"Post-Install Error",
&format!("Remote config HTTP Error: {}", resp.status()),
@@ -241,26 +270,29 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
}
if let Some(steps) = final_steps {
let _ = handle.emit(
"install-status",
InstallProgress {
id: task_id.clone(),
status: "configuring".to_string(),
progress: 1.0,
},
emit_task_event(
&runtime_handle,
&log_id,
&task_id,
"install",
"running",
"configuring",
1.0,
task_version.clone(),
Some("Starting post-installation configuration".to_string()),
);
emit_log(
&handle,
&runtime_handle,
&log_id,
"Post-Install",
"Starting post-installation configuration...",
"info",
);
if let Err(e) = execute_post_install(&handle, &log_id, steps).await {
emit_log(&handle, &log_id, "Post-Install Error", &e, "error");
if let Err(e) = execute_post_install(&runtime_handle, &log_id, steps).await {
emit_log(&runtime_handle, &log_id, "Post-Install Error", &e, "error");
} else {
emit_log(
&handle,
&runtime_handle,
&log_id,
"Post-Install",
"Post-installation configuration completed.",
@@ -273,21 +305,35 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
status_result
}
Err(e) => {
emit_log(&handle, &log_id, "Fatal Error", &e.to_string(), "error");
emit_log(&runtime_handle, &log_id, "Fatal Error", &e.to_string(), "error");
emit_task_event(
&runtime_handle,
&log_id,
&task_id,
"install",
"failed",
"spawn_error",
0.0,
task_version.clone(),
Some(e.to_string()),
);
"error"
}
};
let _ = handle.emit(
"install-status",
InstallProgress {
id: task_id.clone(),
status: status_result.to_string(),
progress: 1.0,
},
emit_task_event(
&runtime_handle,
&log_id,
&task_id,
"install",
if status_result == "success" { "completed" } else { "failed" },
status_result,
1.0,
task_version.clone(),
Some(format!("Execution finished: {}", status_result)),
);
emit_log(
&handle,
&runtime_handle,
&log_id,
"Result",
&format!("Execution finished: {}", status_result),
@@ -304,7 +350,7 @@ pub fn create_install_state(handle: AppHandle) -> AppState {
}
});
AppState { install_tx: tx }
AppState { install_tx: tx, app_handle: handle }
}
#[tauri::command]
@@ -312,6 +358,18 @@ pub async fn install_software(
task: InstallTask,
state: State<'_, AppState>,
) -> Result<(), String> {
let log_id = format!("install-{}", task.id);
emit_task_event(
&state.app_handle,
&log_id,
&task.id,
"install",
"queued",
"queued",
0.0,
task.version.clone(),
None,
);
state.install_tx.send(task).await.map_err(|e| e.to_string())
}
@@ -346,6 +404,19 @@ fn spawn_install_stream_reader<R: Read + Send + 'static>(
progress: p_val / 100.0,
},
);
let _ = handle.emit(
"task-event",
TaskEventPayload {
task_id: log_id.clone(),
software_id: task_id.clone(),
task_type: "install".to_string(),
status: "running".to_string(),
stage: "installing".to_string(),
progress: p_val / 100.0,
target_version: None,
message: None,
},
);
is_progress = true;
}
} else if let Some(caps) = size_re.captures(clean_line) {
@@ -360,6 +431,19 @@ fn spawn_install_stream_reader<R: Read + Send + 'static>(
progress: (current / total).min(1.0),
},
);
let _ = handle.emit(
"task-event",
TaskEventPayload {
task_id: log_id.clone(),
software_id: task_id.clone(),
task_type: "install".to_string(),
status: "running".to_string(),
stage: "installing".to_string(),
progress: (current / total).min(1.0),
target_version: None,
message: None,
},
);
is_progress = true;
}
}
@@ -375,6 +459,48 @@ fn spawn_install_stream_reader<R: Read + Send + 'static>(
})
}
fn emit_task_event(
handle: &AppHandle,
task_id: &str,
software_id: &str,
task_type: &str,
status: &str,
stage: &str,
progress: f32,
target_version: Option<String>,
message: Option<String>,
) {
let _ = handle.emit(
"task-event",
TaskEventPayload {
task_id: task_id.to_string(),
software_id: software_id.to_string(),
task_type: task_type.to_string(),
status: status.to_string(),
stage: stage.to_string(),
progress,
target_version: target_version.clone(),
message,
},
);
let legacy_status = match status {
"queued" => "pending".to_string(),
"completed" => "success".to_string(),
"failed" => "error".to_string(),
_ => stage.to_string(),
};
let _ = handle.emit(
"install-status",
InstallProgress {
id: software_id.to_string(),
status: legacy_status,
progress,
},
);
}
fn expand_win_path(path: &str) -> PathBuf {
let mut expanded = path.to_string();
let env_vars = [