///|
/// Normalizes caller-supplied reasons before they cross the FFI boundary.
fn normalize_reason(reason : String) -> String {
let trimmed = reason.trim().to_owned()
if trimmed is "" {
default_reason
} else {
trimmed
}
}
///|
/// Decodes a backend error payload into a readable string.
fn decode_native_detail(detail : Bytes) -> String {
if detail.is_empty() {
"The native backend did not provide extra details."
} else {
@utf8.decode_lossy(detail)
}
}
///|
/// Raises a public keep-awake error from raw native status and detail bytes.
fn[T] raise_native_error(
status~ : Int,
operation~ : String,
detail~ : Bytes,
) -> T raise KeepAwakeError {
raise classify_native_error(
status~,
operation~,
detail=decode_native_detail(detail),
)
}
///|
/// Resolves a newly-created native guard into either a live handle or an error.
fn finish_acquire(handle : Guard) -> Guard raise KeepAwakeError {
if handle.active() {
handle
} else {
raise_native_error(
status=native_guard_status(handle),
operation="acquire",
detail=native_guard_last_error(handle),
)
}
}
///|
/// Raises a release failure if the native backend did not report success.
fn finish_release(status : Int, detail : Bytes) -> Unit raise KeepAwakeError {
if status == native_status_ok {
()
} else {
raise_native_error(status~, operation="release", detail~)
}
}
///|
/// Runs an action and always attempts cleanup before returning or re-raising.
fn[T] run_with_cleanup(
action : () -> T raise,
cleanup : () -> Unit raise,
) -> T raise {
try action() catch {
err => {
cleanup() catch {
_ => ()
}
raise err
}
} noraise {
value => {
cleanup()
value
}
}
}
///|
/// Maps native backend status codes into public MoonBit errors.
fn classify_native_error(
status~ : Int,
operation~ : String,
detail~ : String,
) -> KeepAwakeError {
if status == native_status_backend_unavailable {
BackendUnavailable(detail~)
} else if status == native_status_operation_failed {
OperationFailed(operation~, detail~)
} else {
OperationFailed(operation~, detail~)
}
}