← Commit history

restart_for_addin: no Unsaved changes box when the add-in cannot save (#129)

Colby Knox ·091992a224 ·12d ago ·parent fba2984
2 files changed +38−2
CHANGELOG.md+6
@@ -5,6 +5,12 @@ Canonical: https://wiki.adom.inc/adom/fusion-bridge ยท GitHub backup: `adom-inc/  ## 1.13.28 (unreleased) +- `fusion_restart_for_addin` no longer meets Fusion's Unsaved changes box when the add-in cannot save (issue #129).+  A save whose add-in connection dropped is retried once after 5 s (the add-in can be unreachable for a moment+  right after `bridge_install`). If the add-in still answers its status probe but the save failed, the restart+  stops with `restart_needs_decision` before anything is closed. Fusion is closed without the add-in's save only+  when the add-in is not loaded at all.+ From Aditya Angajala (PR #17): `fusion_export_optimized_glb` produces a publishable molecule, not just a GLB. - It exports the EAGLE `.brd` (new `board` arg, default true) and sends it to service-step2glb as the `board` part,   the same contract as `step2glb convert --board`. With the board the service generates the footprint and symbol
rust/bridge/src/lifecycle.rs+32−2
@@ -194,6 +194,12 @@ pub fn restart_policy(state_dir: &std::path::Path) -> String {     crate::small_verbs::get_pref(state_dir, "restartPolicy").and_then(|v| v.as_str().map(|s| s.to_lowercase())).unwrap_or_else(|| "auto".into()) } +/// The add-in request never reached Fusion (a dropped or refused connection), as opposed to the+/// add-in answering that it could not save.+fn save_unreachable(r: &Value) -> bool {+    r["error"].as_str().map(|e| e.starts_with("Add-in request failed") && !e.contains("non-JSON")).unwrap_or(false)+}+ /// An auto restart stopped short of someone's unsaved work: for the next 30 minutes the gate asks /// (the toast with Save and restart) instead of trying again behind the person's back. pub fn note_auto_restart_declined(state_dir: &std::path::Path, why: &Value) {@@ -380,7 +386,14 @@ pub fn restart_for_addin(ctx: &Ctx, args: &Value, pass: &dyn Fn(&str, Value) ->     let mut graceful = false;     let mut discarded: Vec<String> = Vec::new();     if save {-        let sv = pass("close_all_documents", json!({"save": true}));+        let mut sv = pass("close_all_documents", json!({"save": true}));+        // issue #129: right after bridge_install the add-in's connection can drop for a moment even+        // though it answered seconds before; one retry before treating the save as impossible+        if sv["success"].as_bool() != Some(true) && save_unreachable(&sv) {+            steps.push(json!({"step": "save", "ok": false, "error": sv["error"].as_str().unwrap_or("").chars().take(160).collect::<String>(), "retry": true}));+            std::thread::sleep(Duration::from_secs(5));+            sv = pass("close_all_documents", json!({"save": true}));+        }         steps.push(json!({"step": "save", "ok": sv["success"].as_bool().unwrap_or(false), "closed": sv["data"]["closed"], "wouldPrompt": sv["data"]["wouldPrompt"]}));         let wp: Vec<String> = sv["data"]["wouldPrompt"].as_array().map(|a| a.iter().filter_map(|v| v.as_str().map(str::to_string)).collect()).unwrap_or_default();         if !wp.is_empty() {@@ -399,8 +412,19 @@ pub fn restart_for_addin(ctx: &Ctx, args: &Value, pass: &dyn Fn(&str, Value) ->                     "_hint": "Ask the user where to save it (fusion_save_to_cloud {name, projectName}), then retry. Nothing was closed and no dialog was raised."});             }         }+        if sv["success"].as_bool() != Some(true) && !force && crate::addin::addin_status_probe(Duration::from_secs(10)).is_some() {+            // issue #129: the add-in is loaded and answering but could not save. A close now would+            // only meet Fusion's own Unsaved changes box, so stop here: nothing closed, no dialog.+            let why = json!({"save": sv["errorCode"], "error": sv["error"]});+            if auto_policy { note_auto_restart_declined(&sd, &why); }+            crate::fusion_prefs::action_ledger(&sd, "restart_stopped_short", json!({"steps": steps, "why": why}));+            return json!({"success": false, "errorCode": "restart_needs_decision", "restartAborted": true, "steps": steps,+                "error": format!("Fusion was not restarted: the add-in answers but could not save the open documents ({})", sv["error"].as_str().unwrap_or("no reason given").chars().take(200).collect::<String>()),+                "userMessage": "Fusion needs a restart to load the Adom add-in, and the bridge could not save the open documents first. Save or close them, then restart.",+                "_hint": "Nothing was closed and no dialog was raised. Save the documents (fusion_save_document) or close them, then call fusion_restart_for_addin {save:true} again."});+        }         if sv["success"].as_bool() != Some(true) && auto_policy {-            // the add-in cannot save (it is the thing that is not answering). Close Fusion the way+            // the add-in is not loaded at all (it is the thing that is not answering). Close Fusion the way             // a person would: it closes at once when nothing is unsaved, and a save prompt for             // someone's work stops the restart instead of a kill losing it (force: kill, as before)             steps.push(json!({"step": "save-unavailable", "error": sv["error"].as_str().unwrap_or("").chars().take(160).collect::<String>()}));@@ -549,6 +573,12 @@ pub fn diagnose_addin_timeout(ctx: &Ctx, command: &str, original: &Value) -> Val mod tests {     use super::*;     #[test]+    fn only_a_connection_that_never_reached_fusion_is_retried() {+        assert!(save_unreachable(&json!({"success": false, "error": "Add-in request failed: Network Error: A connection attempt failed"})));+        assert!(!save_unreachable(&json!({"success": false, "error": "Add-in request failed: non-JSON answer"})));+        assert!(!save_unreachable(&json!({"success": false, "error": "could not save lane-BQ25792-copy"})));+    }+    #[test]     fn a_save_prompt_is_answered_only_for_the_bridges_own_document() {         assert_eq!(prompt_answer(true), "dont_save");         assert_eq!(prompt_answer(false), "cancel_and_stop");