import nl.komponents.kovenant.CancelablePromise import nl.komponents.kovenant.Promise import nl.komponents.kovenant.deferred import retrofit2.* import java.lang.reflect.ParameterizedType import java.lang.reflect.Type /** * A [call adapter][CallAdapter.Factory] which creates Kovenant Promises. * * * Adding this class to [Retrofit] allows you to return [Promise] from * service methods. * ` * interface MyService { * @GET("user/me") * Promise<User, Exception> getUser() * } ` * * There are two configurations supported for the `Promise` type parameter: * * * Direct body (e.g., `Promise<User>`) returns the deserialized body for 2XX * responses, sets [HttpException] errors for non-2XX responses, and sets [IOException] * for network errors. * * Response wrapped body (e.g., `Promise<Response<User>>`) returns a * [Response] object for all HTTP responses and sets [IOException] for network * errors * */ class KovenantCallAdapterFactory: CallAdapter.Factory() { override fun get(returnType: Type, annotations: Array, retrofit: Retrofit): CallAdapter<*>? { if (CallAdapter.Factory.getRawType(returnType) != Promise::class.java) { return null } if (returnType !is ParameterizedType) { throw IllegalStateException("Promise return type must be parameterized" + " as Promise") } val innerType = CallAdapter.Factory.getParameterUpperBound(0, returnType) if (CallAdapter.Factory.getRawType(innerType) != Response::class.java) { // Generic type is not Response. Use it for body-only adapter. return BodyCallAdapter(innerType) } // Generic type is Response. Extract T and create the Response version of the adapter. if (innerType !is ParameterizedType) { throw IllegalStateException("Response must be parameterized" + " as Response or Response") } val responseType = CallAdapter.Factory.getParameterUpperBound(0, innerType) return ResponseCallAdapter(responseType) } private class BodyCallAdapter(private val responseType: Type) : CallAdapter> { override fun responseType(): Type { return responseType } override fun adapt(call: Call): Promise { val deferred = deferred() call.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { deferred.resolve(response.body() as Any) } else { deferred.reject(HttpException(response)) } } override fun onFailure(call: Call, error: Throwable) { deferred.reject(error as? Exception ?: Exception(error)) } }) return CancelableCallPromise(deferred.promise, call) } } private class ResponseCallAdapter(private val responseType: Type) : CallAdapter> { override fun responseType(): Type { return responseType } override fun adapt(call: Call): Promise { val deferred = deferred() call.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { deferred.resolve(response as Any) } override fun onFailure(call: Call, error: Throwable) { deferred.reject(error as? Exception ?: Exception(error)) } }) return CancelableCallPromise(deferred.promise, call) } } } private class CancelableCallPromise(private val target: Promise, private val call: Call<*>) : CancelablePromise, Promise by target { override fun cancel(error: Exception): Boolean { call.cancel() return true } }