{#/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#} // 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. // Error runtime. [StructLayout(LayoutKind.Sequential)] struct RustCallStatus { public int code; public RustBuffer error_buf; public bool IsSuccess() { return code == 0; } public bool IsError() { return code == 1; } public bool IsPanic() { return code == 2; } } // Base class for all uniffi exceptions public class UniffiException: Exception { public UniffiException(): base() {} public UniffiException(string message): base(message) {} } public class UndeclaredErrorException: UniffiException { public UndeclaredErrorException(string message): base(message) {} } public class PanicException: UniffiException { public PanicException(string message): base(message) {} } public class AllocationException: UniffiException { public AllocationException(string message): base(message) {} } public class InternalException: UniffiException { public InternalException(string message): base(message) {} } public class InvalidEnumException: InternalException { public InvalidEnumException(string message): base(message) { } } // Each top-level error class has a companion object that can lift the error from the call status's rust buffer interface CallStatusErrorHandler where E: Exception { E Lift(RustBuffer error_buf); } // CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR class NullCallStatusErrorHandler: CallStatusErrorHandler { public static NullCallStatusErrorHandler INSTANCE = new NullCallStatusErrorHandler(); public UniffiException Lift(RustBuffer error_buf) { RustBuffer.Free(error_buf); return new UndeclaredErrorException("library has returned an error not declared in UNIFFI interface file"); } } // Helpers for calling Rust // In practice we usually need to be synchronized to call this safely, so it doesn't // synchronize itself class _UniffiHelpers { public delegate void RustCallAction(ref RustCallStatus status); public delegate U RustCallFunc(ref RustCallStatus status); // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err public static U RustCallWithError(CallStatusErrorHandler errorHandler, RustCallFunc callback) where E: UniffiException { var status = new RustCallStatus(); var return_value = callback(ref status); if (status.IsSuccess()) { return return_value; } 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 new PanicException({{ TypeIdentifier::String.borrow()|lift_fn }}(status.error_buf)); } else { throw new PanicException("Rust panic"); } } else { throw new InternalException($"Unknown rust call status: {status.code}"); } } // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err public static void RustCallWithError(CallStatusErrorHandler errorHandler, RustCallAction callback) where E: UniffiException { _UniffiHelpers.RustCallWithError(errorHandler, (ref RustCallStatus status) => { callback(ref status); return 0; }); } // Call a rust function that returns a plain value public static U RustCall(RustCallFunc callback) { return _UniffiHelpers.RustCallWithError(NullCallStatusErrorHandler.INSTANCE, callback); } // Call a rust function that returns a plain value public static void RustCall(RustCallAction callback) { _UniffiHelpers.RustCall((ref RustCallStatus status) => { callback(ref status); return 0; }); } }