mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 08:02:24 +00:00
fix(ui): explain the io errors that build their own message
`explain_io` exists because "Permission denied (os error 13)" answers a developer's question and not the reader's — its own doc says so. Every failure routed through `HostOps::notify_err` gets it. Four that build their notification string by hand did not, and printed the raw error. The clearest symptom was inside one file: saving a file in the editor went through `notify_err` and explained itself, while opening the same file, denied for the same reason, said `os error 13`. Both file-link openers are changed together — the file tree's opener deliberately shares its wording with the terminal's (#542), so explaining one and not the other would have split a pairing that was on purpose. The `log::warn!` next to each call keeps the exact error. A developer reading a log and a person who just lost a save want different things, so both are written rather than one chosen — noted on `explain_io` along with the rule, since it was the absence of a stated rule that let four callers drift.
This commit is contained in:
@@ -2943,7 +2943,10 @@ impl TerminalView {
|
||||
) {
|
||||
log::warn!("failed to open file link {}: {reason}", path.display());
|
||||
let path = path.display().to_string();
|
||||
let reason = reason.to_string();
|
||||
// The log above keeps the exact error for whoever is debugging; the
|
||||
// notification gets the sentence, because "os error 13" is not an
|
||||
// answer to the question the reader actually has.
|
||||
let reason = crate::ui::host_ops::explain_io(reason);
|
||||
window.push_notification(
|
||||
crate::ui::i18n::t_fmt(
|
||||
crate::ui::i18n::L10nKey::LinkFileOpenFailed,
|
||||
|
||||
@@ -494,7 +494,10 @@ impl Tty7App {
|
||||
Err(e) => {
|
||||
return Err(t_fmt(
|
||||
L10nKey::EditorCantOpen,
|
||||
&[("path", &path.display().to_string()), ("e", &e.to_string())],
|
||||
&[
|
||||
("path", &path.display().to_string()),
|
||||
("e", &crate::ui::host_ops::explain_io(&e)),
|
||||
],
|
||||
));
|
||||
}
|
||||
};
|
||||
@@ -512,7 +515,10 @@ impl Tty7App {
|
||||
Err(e) => {
|
||||
return Err(t_fmt(
|
||||
L10nKey::EditorCantRead,
|
||||
&[("path", &path.display().to_string()), ("e", &e.to_string())],
|
||||
&[
|
||||
("path", &path.display().to_string()),
|
||||
("e", &crate::ui::host_ops::explain_io(&e)),
|
||||
],
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -1051,7 +1051,7 @@ impl Tty7App {
|
||||
L10nKey::LinkFileOpenFailed,
|
||||
&[
|
||||
("path", &path.display().to_string()),
|
||||
("error", &e.to_string()),
|
||||
("error", &crate::ui::host_ops::explain_io(&e)),
|
||||
],
|
||||
),
|
||||
cx,
|
||||
|
||||
@@ -190,6 +190,15 @@ impl HostOps {
|
||||
/// rest. `Display` on an `io::Error` answers "what happened" for a developer
|
||||
/// reading a log; it does not answer "what now" for the person who just lost
|
||||
/// a save, and "Permission denied (os error 13)" is the shape of that gap.
|
||||
///
|
||||
/// This is the only way an `io::Error` should reach a person. Everything that
|
||||
/// goes through [`HostOps::notify_err`] gets it for free; the handful of
|
||||
/// places that build their own string have to ask. Opening a file used to
|
||||
/// print `os error 13` while saving the same file explained itself, because
|
||||
/// the save went through `notify_err` and the open did not.
|
||||
///
|
||||
/// The raw error still belongs in the log next to the call — the two readers
|
||||
/// want different things, so both are written rather than one chosen.
|
||||
pub fn explain_io(err: &std::io::Error) -> String {
|
||||
use std::io::ErrorKind;
|
||||
let key = match err.kind() {
|
||||
|
||||
Reference in New Issue
Block a user