git.iko.soy/opencode-android
on 06fd41b help sign in
78 lines 2.7 kB history raw
1package soy.iko.opencode.data.repo
3import io.ktor.client.plugins.ClientRequestException
4import io.ktor.client.plugins.HttpRequestTimeoutException
5import io.ktor.client.plugins.RedirectResponseException
6import io.ktor.client.plugins.ResponseException
7import io.ktor.client.plugins.ServerResponseException
8import java.io.IOException
9import java.net.ConnectException
10import java.net.SocketTimeoutException
11import java.net.UnknownHostException
13/**
14 * User-facing categories for a thrown error. Extracted so the classification logic is
15 * unit-testable without an Android [android.content.Context] (the mapping from a kind
16 * to a localized string lives in [friendlyError]).
17 */
18enum class ErrorKind {
19 /** Server host could not be resolved / connection refused. */
20 NOT_REACHABLE,
22 /** Connect or read timed out. */
23 TIMEOUT,
25 /** Server returned a 5xx response. */
26 SERVER,
28 /** Server returned a 4xx response (auth, not-found, etc.). */
29 CLIENT,
31 /** Any other network-level I/O failure. */
32 NETWORK,
34 /** Anything that isn't network-related. */
35 UNKNOWN,
38/**
39 * Classify a throwable into an [ErrorKind] by inspecting concrete exception types rather
40 * than string-matching class names. Unwraps [cause] chains so an exception surface-wrapped
41 * by a library still classifies correctly.
42 */
43fun classifyError(t: Throwable): ErrorKind {
44 var current: Throwable? = t
45 // Walk a bounded cause chain so wrappers (e.g. an IllegalStateException around an
46 // IOException) still classify by their root.
47 var hops = 0
48 while (current != null && hops < 8) {
49 when (current) {
50 is UnknownHostException, is ConnectException -> return ErrorKind.NOT_REACHABLE
51 is SocketTimeoutException, is HttpRequestTimeoutException ->
52 return ErrorKind.TIMEOUT
53 is ServerResponseException -> return ErrorKind.SERVER
54 is ClientRequestException -> return ErrorKind.CLIENT
55 is RedirectResponseException -> return ErrorKind.CLIENT
56 is IOException -> return ErrorKind.NETWORK
57 else -> {
58 // Any other ResponseException (unrecognized status family).
59 if (current is ResponseException) return ErrorKind.SERVER
60 }
61 }
62 current = current.cause
63 hops++
64 }
65 return ErrorKind.UNKNOWN
68/** The HTTP status code carried by a client/server error, if any. */
69fun responseStatusCode(t: Throwable): Int? {
70 var current: Throwable? = t
71 var hops = 0
72 while (current != null && hops < 8) {
73 if (current is ResponseException) return current.response.status.value
74 current = current.cause
75 hops++
76 }
77 return null