Ely Office: six offline-first desktop apps in Rust and GPUI
ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git), ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
This commit is contained in:
817 files changed
+143101
No files matched your search
@@ -0,0 +1,80 @@
|
||||
use std::{
|
||||
panic, process,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
use crate::{AppId, logging};
|
||||
|
||||
static STARTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// The text a failed start shows, in both languages.
|
||||
pub fn message(app: AppId, reason: &str, log: &str) -> String {
|
||||
let name = app.name();
|
||||
format!("{name} 无法启动。\n{name} could not start.\n\n{reason}\n\n日志 / Log: {log}")
|
||||
}
|
||||
|
||||
/// Logs and shows why `app` cannot start, then exits 1.
|
||||
pub fn fail(app: AppId, reason: &str) -> ! {
|
||||
log::error!("{} cannot start: {reason}", app.name());
|
||||
let log = logging::path().map_or_else(
|
||||
|| "none, logging did not start".to_string(),
|
||||
|path| path.display().to_string(),
|
||||
);
|
||||
let text = message(app, reason, &log);
|
||||
eprintln!("{text}");
|
||||
if std::env::var_os("ELY_NO_DIALOG").is_none() {
|
||||
rfd::MessageDialog::new()
|
||||
.set_level(rfd::MessageLevel::Error)
|
||||
.set_title(app.name())
|
||||
.set_description(&text)
|
||||
.show();
|
||||
}
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
/// Until `started`, a panic is a failed start.
|
||||
pub fn guard(app: AppId) {
|
||||
let previous = panic::take_hook();
|
||||
panic::set_hook(Box::new(move |info| {
|
||||
if STARTED.load(Ordering::Relaxed) {
|
||||
previous(info);
|
||||
} else {
|
||||
fail(
|
||||
app,
|
||||
&format!("the app panicked before its first frame: {info}"),
|
||||
);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/// Ely restores sessions itself; AppKit's "reopen windows?" would block.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn no_window_restore() {
|
||||
use objc2_foundation::{NSString, NSUserDefaults};
|
||||
let key = NSString::from_str("ApplePersistenceIgnoreState");
|
||||
NSUserDefaults::standardUserDefaults().setBool_forKey(true, &key);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub fn no_window_restore() {}
|
||||
|
||||
/// Marks the start as done; later panics are ordinary panics.
|
||||
pub fn started() {
|
||||
STARTED.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::message;
|
||||
use crate::AppId;
|
||||
|
||||
#[test]
|
||||
fn the_message_names_the_app_the_reason_and_the_log_in_both_languages() {
|
||||
let text = message(AppId::Pdf, "no usable GPU", "/logs/ely.log");
|
||||
|
||||
assert!(text.contains("ElyPDF 无法启动"), "{text}");
|
||||
assert!(text.contains("ElyPDF could not start"), "{text}");
|
||||
assert!(text.contains("no usable GPU"), "{text}");
|
||||
assert!(text.contains("/logs/ely.log"), "{text}");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user