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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
// A handful of classes and functions to support the generated data structures.
// This would be a good candidate for isolating in its own ffi-support lib.
internal const val UNIFFI_CALL_SUCCESS = 0.toByte()
internal const val UNIFFI_CALL_ERROR = 1.toByte()
internal const val UNIFFI_CALL_UNEXPECTED_ERROR = 2.toByte()
@Structure.FieldOrder("code", "error_buf")
internal open class UniffiRustCallStatus : Structure() {
@JvmField var code: Byte = 0
@JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue()
class ByValue: UniffiRustCallStatus(), Structure.ByValue
fun isSuccess(): Boolean {
return code == UNIFFI_CALL_SUCCESS
}
fun isError(): Boolean {
return code == UNIFFI_CALL_ERROR
}
fun isPanic(): Boolean {
return code == UNIFFI_CALL_UNEXPECTED_ERROR
}
companion object {
fun create(code: Byte, errorBuf: RustBuffer.ByValue): UniffiRustCallStatus.ByValue {
val callStatus = UniffiRustCallStatus.ByValue()
callStatus.code = code
callStatus.error_buf = errorBuf
return callStatus
}
}
}
class InternalException(message: String) : Exception(message)
// Each top-level error class has a companion object that can lift the error from the call status's rust buffer
interface UniffiRustCallStatusErrorHandler<E> {
fun lift(error_buf: RustBuffer.ByValue): E;
}
// Helpers for calling Rust
// In practice we usually need to be synchronized to call this safely, so it doesn't
// synchronize itself
// Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err
private inline fun <U, E: Exception> uniffiRustCallWithError(errorHandler: UniffiRustCallStatusErrorHandler<E>, callback: (UniffiRustCallStatus) -> U): U {
var status = UniffiRustCallStatus();
val return_value = callback(status)
uniffiCheckCallStatus(errorHandler, status)
return return_value
}
// Check UniffiRustCallStatus and throw an error if the call wasn't successful
private fun<E: Exception> uniffiCheckCallStatus(errorHandler: UniffiRustCallStatusErrorHandler<E>, status: UniffiRustCallStatus) {
if (status.isSuccess()) {
return
} else if (status.isError()) {
throw errorHandler.lift(status.error_buf)
} else if (status.isPanic()) {
// when the rust code sees a panic, it tries to construct a rustbuffer
// with the message. but if that code panics, then it just sends back
// an empty buffer.
if (status.error_buf.len > 0) {
throw InternalException({{ Type::String.borrow()|lift_fn }}(status.error_buf))
} else {
throw InternalException("Rust panic")
}
} else {
throw InternalException("Unknown rust call status: $status.code")
}
}
// UniffiRustCallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR
object UniffiNullRustCallStatusErrorHandler: UniffiRustCallStatusErrorHandler<InternalException> {
override fun lift(error_buf: RustBuffer.ByValue): InternalException {
RustBuffer.free(error_buf)
return InternalException("Unexpected CALL_ERROR")
}
}
// Call a rust function that returns a plain value
private inline fun <U> uniffiRustCall(callback: (UniffiRustCallStatus) -> U): U {
return uniffiRustCallWithError(UniffiNullRustCallStatusErrorHandler, callback);
}
internal inline fun<T> uniffiTraitInterfaceCall(
callStatus: UniffiRustCallStatus,
makeCall: () -> T,
writeReturn: (T) -> Unit,
) {
try {
writeReturn(makeCall())
} catch(e: Exception) {
callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR
callStatus.error_buf = {{ Type::String.borrow()|lower_fn }}(e.toString())
}
}
internal inline fun<T, reified E: Throwable> uniffiTraitInterfaceCallWithError(
callStatus: UniffiRustCallStatus,
makeCall: () -> T,
writeReturn: (T) -> Unit,
lowerError: (E) -> RustBuffer.ByValue
) {
try {
writeReturn(makeCall())
} catch(e: Exception) {
if (e is E) {
callStatus.code = UNIFFI_CALL_ERROR
callStatus.error_buf = lowerError(e)
} else {
callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR
callStatus.error_buf = {{ Type::String.borrow()|lower_fn }}(e.toString())
}
}
}
|