fix(core,android,app): say why a crawl failed, not only which URL

A transport failure's text is reqwest's "error sending request for url
(…)", and the cause sits further down its source chain. Error::describe
appends the causes and drops the URL, so the reason fits on a share row.
This commit is contained in:
nvrl committed 2026-10-13 20:45:14 +02:00
1 parent 4f130d1d6e
commit 85bc975f5f
3 files changed
+55 -3

No files matched your search

+2 -2
View File
@@ -2637,11 +2637,11 @@ impl AndroidEngine {
if let Err(error) = self
.catalog
.lock()
.record_crawl_failure(&id, &error.to_string())
.record_crawl_failure(&id, &error.describe())
{
log::warn!("record a failed crawl: {error}");
}
return Err(BridgeError::from_display(error));
return Err(BridgeError::from_display(error.describe()));
}
};
let rows = build_rows(&id, &nodes);
+1 -1
View File
@@ -1494,7 +1494,7 @@ impl Engine {
if let Err(error) = self
.catalog
.lock()
.record_crawl_failure(&share_id, &error.to_string())
.record_crawl_failure(&share_id, &error.describe())
{
tracing::warn!(%error, "record a failed crawl");
}
+52
View File
@@ -54,6 +54,36 @@ pub enum Error {
}
impl Error {
/// The message, followed by every cause its own text leaves out.
///
/// A dropped connection reads as reqwest's "error sending request for url
/// (…)" and nothing else: why — a reset, a DNS failure, a certificate — is
/// further down the source chain. That is the part a viewer reporting a
/// failed crawl needs, so the URL, which only says which folder, is left
/// out to keep it on screen.
pub fn describe(&self) -> String {
let mut text = match self {
Self::Drive(proton_sdk::error::ProtonError::Transport(error)) => {
let url = error.url().map(|url| format!(" for url ({url})"));
let own = self.to_string();
url.map_or_else(|| own.clone(), |url| own.replace(&url, ""))
}
_ => self.to_string(),
};
let mut seen = self.to_string();
let mut source = std::error::Error::source(self);
while let Some(cause) = source {
let line = cause.to_string();
if !seen.contains(&line) {
text.push_str(": ");
text.push_str(&line);
seen.push_str(&line);
}
source = cause.source();
}
text
}
/// Whether this is the account's session having ended: the refresh token
/// was revoked or expired, or the token lacks a scope only a password
/// sign-in grants. Nothing but signing in again fixes it.
@@ -76,3 +106,25 @@ impl From<keyring::Error> for Error {
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, thiserror::Error)]
#[error("connection closed before message completed")]
struct Closed;
#[derive(Debug, thiserror::Error)]
#[error("client error (SendRequest)")]
struct SendRequest(#[source] Closed);
#[test]
fn a_failure_is_described_with_the_causes_its_own_text_leaves_out() {
let error = Error::Io(std::io::Error::other(SendRequest(Closed)));
assert_eq!(
error.describe(),
"io: client error (SendRequest): connection closed before message completed"
);
}
}