-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use sealed interfaces to organize and enumerate all errors
- Loading branch information
Showing
18 changed files
with
513 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
turbo/src/main/kotlin/dev/hotwire/turbo/errors/HttpError.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package dev.hotwire.turbo.errors | ||
|
||
import android.webkit.WebResourceResponse | ||
|
||
sealed interface HttpError : TurboVisitError { | ||
val statusCode: Int | ||
val reasonPhrase: String? | ||
|
||
sealed interface ClientError : HttpError { | ||
data object BadRequest : ClientError { | ||
override val statusCode = 400 | ||
override val reasonPhrase = "Bad Request" | ||
} | ||
|
||
data object Unauthorized : ClientError { | ||
override val statusCode = 401 | ||
override val reasonPhrase = "Unauthorized" | ||
} | ||
|
||
data object Forbidden : ClientError { | ||
override val statusCode = 403 | ||
override val reasonPhrase = "Forbidden" | ||
} | ||
|
||
data object NotFound : ClientError { | ||
override val statusCode = 404 | ||
override val reasonPhrase = "Not Found" | ||
} | ||
|
||
data object MethodNotAllowed : ClientError { | ||
override val statusCode = 405 | ||
override val reasonPhrase = "Method Not Allowed" | ||
} | ||
|
||
data object NotAccessible : ClientError { | ||
override val statusCode = 406 | ||
override val reasonPhrase = "Not Accessible" | ||
} | ||
|
||
data object ProxyAuthenticationRequired : ClientError { | ||
override val statusCode = 407 | ||
override val reasonPhrase = "Proxy Authentication Required" | ||
} | ||
|
||
data object RequestTimeout : ClientError { | ||
override val statusCode = 408 | ||
override val reasonPhrase = "Request Timeout" | ||
} | ||
|
||
data object Conflict : ClientError { | ||
override val statusCode = 409 | ||
override val reasonPhrase = "Conflict" | ||
} | ||
|
||
data object MisdirectedRequest : ClientError { | ||
override val statusCode = 421 | ||
override val reasonPhrase = "Misdirected Request" | ||
} | ||
|
||
data object UnprocessableEntity : ClientError { | ||
override val statusCode = 422 | ||
override val reasonPhrase = "Unprocessable Entity" | ||
} | ||
|
||
data object PreconditionRequired : ClientError { | ||
override val statusCode = 428 | ||
override val reasonPhrase = "Precondition Required" | ||
} | ||
|
||
data object TooManyRequests : ClientError { | ||
override val statusCode = 429 | ||
override val reasonPhrase = "Too Many Requests" | ||
} | ||
|
||
data class Other( | ||
override val statusCode: Int, | ||
override val reasonPhrase: String? | ||
) : ClientError | ||
} | ||
|
||
sealed interface ServerError : HttpError { | ||
data object InternalServerError : ServerError { | ||
override val statusCode = 500 | ||
override val reasonPhrase = "Internal Server Error" | ||
} | ||
|
||
data object NotImplemented : ServerError { | ||
override val statusCode = 501 | ||
override val reasonPhrase = "Not Implemented" | ||
} | ||
|
||
data object BadGateway : ServerError { | ||
override val statusCode = 502 | ||
override val reasonPhrase = "Bad Gateway" | ||
} | ||
|
||
data object ServiceUnavailable : ServerError { | ||
override val statusCode = 503 | ||
override val reasonPhrase = "Service Unavailable" | ||
} | ||
|
||
data object GatewayTimeout : ServerError { | ||
override val statusCode = 504 | ||
override val reasonPhrase = "Gateway Timeout" | ||
} | ||
|
||
data object HttpVersionNotSupported : ServerError { | ||
override val statusCode = 505 | ||
override val reasonPhrase = "Http Version Not Supported" | ||
} | ||
|
||
data class Other( | ||
override val statusCode: Int, | ||
override val reasonPhrase: String? | ||
) : ServerError | ||
} | ||
|
||
companion object { | ||
fun from(errorResponse: WebResourceResponse): HttpError { | ||
return getError(errorResponse.statusCode, errorResponse.reasonPhrase) | ||
} | ||
|
||
fun from(statusCode: Int): HttpError { | ||
return getError(statusCode, null) | ||
} | ||
|
||
private fun getError(statusCode: Int, reasonPhrase: String?): HttpError { | ||
if (statusCode in 400..499) { | ||
return ClientError::class.sealedSubclasses | ||
.mapNotNull { it.objectInstance } | ||
.firstOrNull { it.statusCode == statusCode } | ||
?: ClientError.Other(statusCode, reasonPhrase) | ||
} | ||
|
||
if (statusCode in 500..599) { | ||
return ServerError::class.sealedSubclasses | ||
.map { it.objectInstance } | ||
.firstOrNull { it?.statusCode == statusCode } | ||
?: ServerError.Other(statusCode, reasonPhrase) | ||
} | ||
|
||
throw IllegalArgumentException("Invalid HTTP error status code: $statusCode") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package dev.hotwire.turbo.errors | ||
|
||
sealed interface LoadError : TurboVisitError { | ||
data object NotPresent : LoadError | ||
data object NotReady : LoadError | ||
} |
3 changes: 3 additions & 0 deletions
3
turbo/src/main/kotlin/dev/hotwire/turbo/errors/TurboVisitError.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package dev.hotwire.turbo.errors | ||
|
||
sealed interface TurboVisitError |
125 changes: 125 additions & 0 deletions
125
turbo/src/main/kotlin/dev/hotwire/turbo/errors/WebError.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package dev.hotwire.turbo.errors | ||
|
||
import androidx.webkit.WebResourceErrorCompat | ||
import androidx.webkit.WebViewClientCompat | ||
import androidx.webkit.WebViewFeature | ||
import androidx.webkit.WebViewFeature.isFeatureSupported | ||
|
||
sealed interface WebError : TurboVisitError { | ||
val errorCode: Int | ||
val description: String? | ||
|
||
data object Unknown : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_UNKNOWN | ||
override val description = "Unknown" | ||
} | ||
|
||
data object HostLookup : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_HOST_LOOKUP | ||
override val description = "Host Lookup" | ||
} | ||
|
||
data object UnsupportedAuthScheme : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_UNSUPPORTED_AUTH_SCHEME | ||
override val description = "Unsupported Auth Scheme" | ||
} | ||
|
||
data object Authentication : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_AUTHENTICATION | ||
override val description = "Authentication" | ||
} | ||
|
||
data object ProxyAuthentication : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_PROXY_AUTHENTICATION | ||
override val description = "Proxy Authentication" | ||
} | ||
|
||
data object Connect : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_CONNECT | ||
override val description = "Connect" | ||
} | ||
|
||
data object IO : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_IO | ||
override val description = "IO" | ||
} | ||
|
||
data object Timeout : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_TIMEOUT | ||
override val description = "Timeout" | ||
} | ||
|
||
data object RedirectLoop : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_REDIRECT_LOOP | ||
override val description = "Redirect Loop" | ||
} | ||
|
||
data object UnsupportedScheme : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_UNSUPPORTED_SCHEME | ||
override val description = "Unsupported Scheme" | ||
} | ||
|
||
data object FailedSslHandshake : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_FAILED_SSL_HANDSHAKE | ||
override val description = "Failed SSL Handshake" | ||
} | ||
|
||
data object BadUrl : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_BAD_URL | ||
override val description = "Bad URL" | ||
} | ||
|
||
data object File : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_FILE | ||
override val description = "File" | ||
} | ||
|
||
data object FileNotFound : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_FILE_NOT_FOUND | ||
override val description = "File Not Found" | ||
} | ||
|
||
data object TooManyRequests : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_TOO_MANY_REQUESTS | ||
override val description = "Too Many Requests" | ||
} | ||
|
||
data object UnsafeResource : WebError { | ||
override val errorCode = WebViewClientCompat.ERROR_UNSAFE_RESOURCE | ||
override val description = "Unsafe Resource" | ||
} | ||
|
||
data class Other( | ||
override val errorCode: Int, | ||
override val description: String? | ||
) : WebError | ||
|
||
companion object { | ||
fun from(error: WebResourceErrorCompat): WebError { | ||
val errorCode = if (isFeatureSupported(WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE)) { | ||
error.errorCode | ||
} else { | ||
0 | ||
} | ||
|
||
val description = if (isFeatureSupported(WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION)) { | ||
error.description.toString() | ||
} else { | ||
null | ||
} | ||
|
||
return getError(errorCode, description) | ||
} | ||
|
||
fun from(errorCode: Int): WebError { | ||
return getError(errorCode, null) | ||
} | ||
|
||
private fun getError(errorCode: Int, description: String?): WebError { | ||
return WebError::class.sealedSubclasses | ||
.mapNotNull { it.objectInstance } | ||
.firstOrNull { it.errorCode == errorCode } | ||
?: Other(errorCode, description) | ||
} | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
turbo/src/main/kotlin/dev/hotwire/turbo/errors/WebSslError.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package dev.hotwire.turbo.errors | ||
|
||
import android.net.http.SslError | ||
|
||
sealed interface WebSslError : TurboVisitError { | ||
val errorCode: Int | ||
val description: String? | ||
|
||
data object NotYetValid : WebSslError { | ||
override val errorCode = SslError.SSL_NOTYETVALID | ||
override val description = "Not Yet Valid" | ||
} | ||
|
||
data object Expired : WebSslError { | ||
override val errorCode = SslError.SSL_EXPIRED | ||
override val description = "Expired" | ||
} | ||
|
||
data object IdMismatch : WebSslError { | ||
override val errorCode = SslError.SSL_IDMISMATCH | ||
override val description = "ID Mismatch" | ||
} | ||
|
||
data object Untrusted : WebSslError { | ||
override val errorCode = SslError.SSL_UNTRUSTED | ||
override val description = "Untrusted" | ||
} | ||
|
||
data object DateInvalid : WebSslError { | ||
override val errorCode = SslError.SSL_DATE_INVALID | ||
override val description = "Date Invalid" | ||
} | ||
|
||
data object Invalid : WebSslError { | ||
override val errorCode = SslError.SSL_INVALID | ||
override val description = "Invalid" | ||
} | ||
|
||
data class Other(override val errorCode: Int) : WebSslError { | ||
override val description = null | ||
} | ||
|
||
companion object { | ||
fun from(error: SslError): WebSslError { | ||
return getError(error.primaryError) | ||
} | ||
|
||
fun from(errorCode: Int): WebSslError { | ||
return getError(errorCode) | ||
} | ||
|
||
private fun getError(errorCode: Int): WebSslError { | ||
return WebSslError::class.sealedSubclasses | ||
.mapNotNull { it.objectInstance } | ||
.firstOrNull { it.errorCode == errorCode } | ||
?: Other(errorCode) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.