blob: 532b109bda36c5cdf0a2cdfeffecf8106dc75146 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package org.terst.feedreader.readability
/**
* Why an extraction did not produce article content.
*
* Extraction used to fail silently: ReaderScreen did `extracted?.content?.let { }`,
* so a null result — whatever caused it — left the reader showing the raw feed
* content with nothing logged and nothing shown. The only way a failure ever
* surfaced was the user noticing and filing a bug report by hand, which could
* not say why either.
*/
sealed interface ExtractionFailure {
/** The page load reported a transport-level error. */
data class LoadError(val code: Int, val description: String?) : ExtractionFailure
/** The server answered, but with an error status. */
data class HttpError(val statusCode: Int, val reason: String?) : ExtractionFailure
/** The page never finished loading within the budget. */
data class TimedOut(val millis: Long) : ExtractionFailure
/** Readability itself threw, or returned something unusable. */
data class ReadabilityError(val message: String) : ExtractionFailure
/** Readability ran but found no article body worth showing. */
data object NoContent : ExtractionFailure
/** The JS result could not be decoded. */
data class BadResult(val raw: String) : ExtractionFailure
}
private const val MILLIS_PER_SECOND = 1_000
/**
* How much of the raw JS payload a diagnostic keeps. The result can be tens of
* kilobytes; a bug report needs enough to recognise the shape of it, not all.
*/
private const val RAW_DIAGNOSTIC_CHARS = 200
/** A short, user-facing explanation. Shown in a snackbar and in bug reports. */
fun ExtractionFailure.userMessage(): String = when (this) {
is ExtractionFailure.LoadError ->
"Couldn't load the page" + (description?.takeIf { it.isNotBlank() }?.let { " ($it)" } ?: "")
is ExtractionFailure.HttpError -> "Server returned HTTP $statusCode"
is ExtractionFailure.TimedOut -> "Page took longer than ${millis / MILLIS_PER_SECOND}s to load"
is ExtractionFailure.ReadabilityError -> "Couldn't parse the article ($message)"
ExtractionFailure.NoContent -> "No article text found on the page"
is ExtractionFailure.BadResult -> "Unexpected result from the page"
}
/**
* A longer form for the bug report, carrying the detail the short message drops.
* Bug reports are the only channel for a failure the user has already lost, so
* this keeps the raw specifics rather than the tidy summary.
*/
fun ExtractionFailure.diagnostic(): String = when (this) {
is ExtractionFailure.LoadError -> "LoadError code=$code description=${description ?: "none"}"
is ExtractionFailure.HttpError -> "HttpError status=$statusCode reason=${reason ?: "none"}"
is ExtractionFailure.TimedOut -> "TimedOut after ${millis}ms"
is ExtractionFailure.ReadabilityError -> "ReadabilityError $message"
ExtractionFailure.NoContent -> "NoContent (Readability returned no usable body)"
is ExtractionFailure.BadResult -> "BadResult raw=${raw.take(RAW_DIAGNOSTIC_CHARS)}"
}
|