diff --git a/.gitignore b/.gitignore index dc1e685..8792323 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ target .idea out -bin -obj .direnv .envrc result diff --git a/Cargo.toml b/Cargo.toml index ddc5168..9eae2e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ resolver = "2" members = [ "crates/servicepoint", "crates/servicepoint_binding_c", - "crates/servicepoint_binding_c/examples/lang_c" + "crates/servicepoint_binding_c/examples/lang_c", + "crates/servicepoint_binding_uniffi" ] [workspace.package] diff --git a/crates/servicepoint_binding_uniffi/Cargo.toml b/crates/servicepoint_binding_uniffi/Cargo.toml new file mode 100644 index 0000000..5ba77a5 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "servicepoint_binding_uniffi" +version.workspace = true +publish = false +edition = "2021" +license = "GPL-3.0-or-later" +description = "C bindings for the servicepoint crate." +homepage = "https://docs.rs/crate/servicepoint_binding_c" +repository = "https://github.com/cccb/servicepoint" +#readme = "README.md" + +[lib] +crate-type = ["cdylib"] + +[build-dependencies] +uniffi = { version = "0.25.0" , features = [ "build" ] } + +[dependencies] +uniffi = { version = "0.25.0" } +thiserror = "1.0.66" + +[dependencies.servicepoint] +version = "0.11.0" +path = "../servicepoint" +features = ["all_compressions"] + +[dependencies.uniffi-bindgen-cs] +git="https://github.com/NordSecurity/uniffi-bindgen-cs" +# tag="v0.8.3+v0.25.0" +rev="f68639fbc720b50ebe561ba75c66c84dc456bdce" +optional=true + +[lints] +#workspace = true + +[package.metadata.docs.rs] +all-features = true + +[[bin]] +name = "uniffi-bindgen" +required-features = ["uniffi/cli"] + +[[bin]] +name = "uniffi-bindgen-cs" +required-features= ["cs"] + +[features] +default = [] +cs = ["dep:uniffi-bindgen-cs"] diff --git a/crates/servicepoint_binding_uniffi/generate-bindings.sh b/crates/servicepoint_binding_uniffi/generate-bindings.sh new file mode 100755 index 0000000..97ad72e --- /dev/null +++ b/crates/servicepoint_binding_uniffi/generate-bindings.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set +x + +cargo build --release + +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +TARGETPATH="$(realpath $SCRIPTPATH/../../target/release/)" +SERVICEPOINT_SO="$TARGETPATH/libservicepoint_binding_uniffi.so" +CONFIG_TOML="$(realpath $SCRIPTPATH/../uniffi.toml)" + +BINDGEN="cargo run --features=uniffi/cli --bin uniffi-bindgen -- " +BINDGEN_CS="cargo run --features=cs --bin uniffi-bindgen-cs -- " +COMMON_ARGS="--library $SERVICEPOINT_SO" + +${BINDGEN} generate $COMMON_ARGS --language python --out-dir libraries/python +${BINDGEN} generate $COMMON_ARGS --language kotlin --out-dir libraries/kotlin +${BINDGEN} generate $COMMON_ARGS --language swift --out-dir libraries/swift +${BINDGEN_CS} $COMMON_ARGS --out-dir libraries/csharp/ServicePoint diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/.gitignore b/crates/servicepoint_binding_uniffi/libraries/csharp/.gitignore new file mode 100644 index 0000000..1746e32 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/.gitignore b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/.gitignore new file mode 100644 index 0000000..1746e32 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/Program.cs b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/Program.cs new file mode 100644 index 0000000..edca01c --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/Program.cs @@ -0,0 +1,5 @@ +using ServicePoint; + +var connection = new Connection(""); + +var clear = new Clear(); diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/ServicePoint.Example.csproj b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/ServicePoint.Example.csproj new file mode 100644 index 0000000..3e99664 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/ServicePoint.Example.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + ServicePoint.Example + disable + enable + + + + + + + diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/ServicePoint.Tests.csproj b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/ServicePoint.Tests.csproj new file mode 100644 index 0000000..50500b6 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/ServicePoint.Tests.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + disable + enable + + false + true + + + + + + + + + + + + + + + + + + diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/UnitTest1.cs b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/UnitTest1.cs new file mode 100644 index 0000000..46991d7 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/UnitTest1.cs @@ -0,0 +1,12 @@ +using ServicePoint; + +namespace ServicePoint.Tests; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + Assert.Throws(() => new Connection("")); + } +} diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/ServicePoint.csproj b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/ServicePoint.csproj new file mode 100644 index 0000000..5b7dc28 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/ServicePoint.csproj @@ -0,0 +1,53 @@ + + + + net8.0 + disable + enable + true + + + + ServicePoint + 0.10.0 + Repository Authors + None + ServicePoint + CCCB + + C# bindings for the rust crate servicepoint. You will need a suitable native shared library to use this. + For documentation, see the rust documentation: https://docs.rs/servicepoint/latest/servicepoint/. + Note that this library is still in early development. Breaking changes are expected before 1.0 is released. + + README.md + true + + + + + + + + + + + + + + libservicepoint_binding_uniffi.so + + + + + libservicepoint_binding_uniffi.so + + + + + + + + + + + diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/servicepoint_binding_uniffi.cs b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/servicepoint_binding_uniffi.cs new file mode 100644 index 0000000..5e90f6c --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/servicepoint_binding_uniffi.cs @@ -0,0 +1,1077 @@ +// +// This file was generated by uniffi-bindgen-cs v0.8.3+v0.25.0 +// See https://github.com/NordSecurity/uniffi-bindgen-cs for more information. +// + +#nullable enable + + + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +namespace ServicePoint; + + + +// This is a helper for safely working with byte buffers returned from the Rust code. +// A rust-owned buffer is represented by its capacity, its current length, and a +// pointer to the underlying data. + +[StructLayout(LayoutKind.Sequential)] +internal struct RustBuffer { + public int capacity; + public int len; + public IntPtr data; + + public static RustBuffer Alloc(int size) { + return _UniffiHelpers.RustCall((ref RustCallStatus status) => { + var buffer = _UniFFILib.ffi_servicepoint_binding_uniffi_rustbuffer_alloc(size, ref status); + if (buffer.data == IntPtr.Zero) { + throw new AllocationException($"RustBuffer.Alloc() returned null data pointer (size={size})"); + } + return buffer; + }); + } + + public static void Free(RustBuffer buffer) { + _UniffiHelpers.RustCall((ref RustCallStatus status) => { + _UniFFILib.ffi_servicepoint_binding_uniffi_rustbuffer_free(buffer, ref status); + }); + } + + public static BigEndianStream MemoryStream(IntPtr data, int length) { + unsafe { + return new BigEndianStream(new UnmanagedMemoryStream((byte*)data.ToPointer(), length)); + } + } + + public BigEndianStream AsStream() { + unsafe { + return new BigEndianStream(new UnmanagedMemoryStream((byte*)data.ToPointer(), len)); + } + } + + public BigEndianStream AsWriteableStream() { + unsafe { + return new BigEndianStream(new UnmanagedMemoryStream((byte*)data.ToPointer(), capacity, capacity, FileAccess.Write)); + } + } +} + +// This is a helper for safely passing byte references into the rust code. +// It's not actually used at the moment, because there aren't many things that you +// can take a direct pointer to managed memory, and if we're going to copy something +// then we might as well copy it into a `RustBuffer`. But it's here for API +// completeness. + +[StructLayout(LayoutKind.Sequential)] +internal struct ForeignBytes { + public int length; + public IntPtr data; +} + + +// The FfiConverter interface handles converter types to and from the FFI +// +// All implementing objects should be public to support external types. When a +// type is external we need to import it's FfiConverter. +internal abstract class FfiConverter { + // Convert an FFI type to a C# type + public abstract CsType Lift(FfiType value); + + // Convert C# type to an FFI type + public abstract FfiType Lower(CsType value); + + // Read a C# type from a `ByteBuffer` + public abstract CsType Read(BigEndianStream stream); + + // Calculate bytes to allocate when creating a `RustBuffer` + // + // This must return at least as many bytes as the write() function will + // write. It can return more bytes than needed, for example when writing + // Strings we can't know the exact bytes needed until we the UTF-8 + // encoding, so we pessimistically allocate the largest size possible (3 + // bytes per codepoint). Allocating extra bytes is not really a big deal + // because the `RustBuffer` is short-lived. + public abstract int AllocationSize(CsType value); + + // Write a C# type to a `ByteBuffer` + public abstract void Write(CsType value, BigEndianStream stream); + + // Lower a value into a `RustBuffer` + // + // This method lowers a value into a `RustBuffer` rather than the normal + // FfiType. It's used by the callback interface code. Callback interface + // returns are always serialized into a `RustBuffer` regardless of their + // normal FFI type. + public RustBuffer LowerIntoRustBuffer(CsType value) { + var rbuf = RustBuffer.Alloc(AllocationSize(value)); + try { + var stream = rbuf.AsWriteableStream(); + Write(value, stream); + rbuf.len = Convert.ToInt32(stream.Position); + return rbuf; + } catch { + RustBuffer.Free(rbuf); + throw; + } + } + + // Lift a value from a `RustBuffer`. + // + // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. + // It's currently only used by the `FfiConverterRustBuffer` class below. + protected CsType LiftFromRustBuffer(RustBuffer rbuf) { + var stream = rbuf.AsStream(); + try { + var item = Read(stream); + if (stream.HasRemaining()) { + throw new InternalException("junk remaining in buffer after lifting, something is very wrong!!"); + } + return item; + } finally { + RustBuffer.Free(rbuf); + } + } +} + +// FfiConverter that uses `RustBuffer` as the FfiType +internal abstract class FfiConverterRustBuffer: FfiConverter { + public override CsType Lift(RustBuffer value) { + return LiftFromRustBuffer(value); + } + public override RustBuffer Lower(CsType value) { + return LowerIntoRustBuffer(value); + } +} + + +// 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 sbyte 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) { + } +} + +public class UniffiContractVersionException: UniffiException { + public UniffiContractVersionException(string message): base(message) { + } +} + +public class UniffiContractChecksumException: UniffiException { + public UniffiContractChecksumException(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(FfiConverterString.INSTANCE.Lift(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; + }); + } +} + + +// Big endian streams are not yet available in dotnet :'( +// https://github.com/dotnet/runtime/issues/26904 + +class StreamUnderflowException: Exception { + public StreamUnderflowException() { + } +} + +class BigEndianStream { + Stream stream; + public BigEndianStream(Stream stream) { + this.stream = stream; + } + + public bool HasRemaining() { + return (stream.Length - stream.Position) > 0; + } + + public long Position { + get => stream.Position; + set => stream.Position = value; + } + + public void WriteBytes(byte[] value) { + stream.Write(value, 0, value.Length); + } + + public void WriteByte(byte value) { + stream.WriteByte(value); + } + + public void WriteUShort(ushort value) { + stream.WriteByte((byte)(value >> 8)); + stream.WriteByte((byte)value); + } + + public void WriteUInt(uint value) { + stream.WriteByte((byte)(value >> 24)); + stream.WriteByte((byte)(value >> 16)); + stream.WriteByte((byte)(value >> 8)); + stream.WriteByte((byte)value); + } + + public void WriteULong(ulong value) { + WriteUInt((uint)(value >> 32)); + WriteUInt((uint)value); + } + + public void WriteSByte(sbyte value) { + stream.WriteByte((byte)value); + } + + public void WriteShort(short value) { + WriteUShort((ushort)value); + } + + public void WriteInt(int value) { + WriteUInt((uint)value); + } + + public void WriteFloat(float value) { + unsafe { + WriteInt(*((int*)&value)); + } + } + + public void WriteLong(long value) { + WriteULong((ulong)value); + } + + public void WriteDouble(double value) { + WriteLong(BitConverter.DoubleToInt64Bits(value)); + } + + public byte[] ReadBytes(int length) { + CheckRemaining(length); + byte[] result = new byte[length]; + stream.Read(result, 0, length); + return result; + } + + public byte ReadByte() { + CheckRemaining(1); + return Convert.ToByte(stream.ReadByte()); + } + + public ushort ReadUShort() { + CheckRemaining(2); + return (ushort)(stream.ReadByte() << 8 | stream.ReadByte()); + } + + public uint ReadUInt() { + CheckRemaining(4); + return (uint)(stream.ReadByte() << 24 + | stream.ReadByte() << 16 + | stream.ReadByte() << 8 + | stream.ReadByte()); + } + + public ulong ReadULong() { + return (ulong)ReadUInt() << 32 | (ulong)ReadUInt(); + } + + public sbyte ReadSByte() { + return (sbyte)ReadByte(); + } + + public short ReadShort() { + return (short)ReadUShort(); + } + + public int ReadInt() { + return (int)ReadUInt(); + } + + public float ReadFloat() { + unsafe { + int value = ReadInt(); + return *((float*)&value); + } + } + + public long ReadLong() { + return (long)ReadULong(); + } + + public double ReadDouble() { + return BitConverter.Int64BitsToDouble(ReadLong()); + } + + private void CheckRemaining(int length) { + if (stream.Length - stream.Position < length) { + throw new StreamUnderflowException(); + } + } +} + +// Contains loading, initialization code, +// and the FFI Function declarations in a com.sun.jna.Library. + + +// This is an implementation detail which will be called internally by the public API. +static class _UniFFILib { + static _UniFFILib() { + _UniFFILib.uniffiCheckContractApiVersion(); + _UniFFILib.uniffiCheckApiChecksums(); + + } + + [DllImport("servicepoint_binding_uniffi")] + public static extern void uniffi_servicepoint_binding_uniffi_fn_free_clear( + IntPtr ptr,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern ClearSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_clear_new(ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void uniffi_servicepoint_binding_uniffi_fn_free_command( + IntPtr ptr,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void uniffi_servicepoint_binding_uniffi_fn_free_connection( + IntPtr ptr,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern ConnectionSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_connection_new(RustBuffer @host,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern RustBuffer ffi_servicepoint_binding_uniffi_rustbuffer_alloc(int @size,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern RustBuffer ffi_servicepoint_binding_uniffi_rustbuffer_from_bytes(ForeignBytes @bytes,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rustbuffer_free(RustBuffer @buf,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern RustBuffer ffi_servicepoint_binding_uniffi_rustbuffer_reserve(RustBuffer @buf,int @additional,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_continuation_callback_set(IntPtr @callback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_u8(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_u8(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_u8(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern byte ffi_servicepoint_binding_uniffi_rust_future_complete_u8(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_i8(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_i8(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_i8(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern sbyte ffi_servicepoint_binding_uniffi_rust_future_complete_i8(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_u16(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_u16(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_u16(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern ushort ffi_servicepoint_binding_uniffi_rust_future_complete_u16(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_i16(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_i16(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_i16(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern short ffi_servicepoint_binding_uniffi_rust_future_complete_i16(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_u32(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_u32(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_u32(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern uint ffi_servicepoint_binding_uniffi_rust_future_complete_u32(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_i32(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_i32(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_i32(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern int ffi_servicepoint_binding_uniffi_rust_future_complete_i32(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_u64(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_u64(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_u64(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern ulong ffi_servicepoint_binding_uniffi_rust_future_complete_u64(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_i64(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_i64(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_i64(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern long ffi_servicepoint_binding_uniffi_rust_future_complete_i64(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_f32(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_f32(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_f32(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern float ffi_servicepoint_binding_uniffi_rust_future_complete_f32(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_f64(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_f64(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_f64(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern double ffi_servicepoint_binding_uniffi_rust_future_complete_f64(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_pointer(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_pointer(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_pointer(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern SafeHandle ffi_servicepoint_binding_uniffi_rust_future_complete_pointer(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_rust_buffer(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_rust_buffer(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_rust_buffer(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern RustBuffer ffi_servicepoint_binding_uniffi_rust_future_complete_rust_buffer(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_poll_void(IntPtr @handle,IntPtr @uniffiCallback + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_cancel_void(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_free_void(IntPtr @handle + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void ffi_servicepoint_binding_uniffi_rust_future_complete_void(IntPtr @handle,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_clear_new( + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new( + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern uint ffi_servicepoint_binding_uniffi_uniffi_contract_version( + ); + + + + static void uniffiCheckContractApiVersion() { + var scaffolding_contract_version = _UniFFILib.ffi_servicepoint_binding_uniffi_uniffi_contract_version(); + if (24 != scaffolding_contract_version) { + throw new UniffiContractVersionException($"ServicePoint: uniffi bindings expected version `24`, library returned `{scaffolding_contract_version}`"); + } + } + + static void uniffiCheckApiChecksums() { + { + var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_clear_new(); + if (checksum != 31583) { + throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_clear_new` checksum `31583`, library returned `{checksum}`"); + } + } + { + var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new(); + if (checksum != 63821) { + throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new` checksum `63821`, library returned `{checksum}`"); + } + } + } +} + +// Public interface members begin here. + +#pragma warning disable 8625 + + + + +class FfiConverterString: FfiConverter { + public static FfiConverterString INSTANCE = new FfiConverterString(); + + // Note: we don't inherit from FfiConverterRustBuffer, because we use a + // special encoding when lowering/lifting. We can use `RustBuffer.len` to + // store our length and avoid writing it out to the buffer. + public override string Lift(RustBuffer value) { + try { + var bytes = value.AsStream().ReadBytes(value.len); + return System.Text.Encoding.UTF8.GetString(bytes); + } finally { + RustBuffer.Free(value); + } + } + + public override string Read(BigEndianStream stream) { + var length = stream.ReadInt(); + var bytes = stream.ReadBytes(length); + return System.Text.Encoding.UTF8.GetString(bytes); + } + + public override RustBuffer Lower(string value) { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + var rbuf = RustBuffer.Alloc(bytes.Length); + rbuf.AsWriteableStream().WriteBytes(bytes); + return rbuf; + } + + // TODO(CS) + // We aren't sure exactly how many bytes our string will be once it's UTF-8 + // encoded. Allocate 3 bytes per unicode codepoint which will always be + // enough. + public override int AllocationSize(string value) { + const int sizeForLength = 4; + var sizeForString = value.Length * 3; + return sizeForLength + sizeForString; + } + + public override void Write(string value, BigEndianStream stream) { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + stream.WriteInt(bytes.Length); + stream.WriteBytes(bytes); + } +} + + + + +// `SafeHandle` implements the semantics outlined below, i.e. its thread safe, and the dispose +// method will only be called once, once all outstanding native calls have completed. +// https://github.com/mozilla/uniffi-rs/blob/0dc031132d9493ca812c3af6e7dd60ad2ea95bf0/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt#L31 +// https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.criticalhandle + +public abstract class FFIObject: IDisposable where THandle : FFISafeHandle { + private THandle handle; + + public FFIObject(THandle handle) { + this.handle = handle; + } + + public THandle GetHandle() { + return handle; + } + + public void Dispose() { + handle.Dispose(); + } +} + +public abstract class FFISafeHandle: SafeHandle { + public FFISafeHandle(): base(new IntPtr(0), true) { + } + + public FFISafeHandle(IntPtr pointer): this() { + this.SetHandle(pointer); + } + + public override bool IsInvalid { + get { + return handle.ToInt64() == 0; + } + } + + // TODO(CS) this completely breaks any guarantees offered by SafeHandle.. Extracting + // raw value from SafeHandle puts responsiblity on the consumer of this function to + // ensure that SafeHandle outlives the stream, and anyone who might have read the raw + // value from the stream and are holding onto it. Otherwise, the result might be a use + // after free, or free while method calls are still in flight. + // + // This is also relevant for Kotlin. + // + public IntPtr DangerousGetRawFfiValue() { + return handle; + } +} + +static class FFIObjectUtil { + public static void DisposeAll(params Object?[] list) { + foreach (var obj in list) { + Dispose(obj); + } + } + + // Dispose is implemented by recursive type inspection at runtime. This is because + // generating correct Dispose calls for recursive complex types, e.g. List> + // is quite cumbersome. + private static void Dispose(dynamic? obj) { + if (obj == null) { + return; + } + + if (obj is IDisposable disposable) { + disposable.Dispose(); + return; + } + + var type = obj.GetType(); + if (type != null) { + if (type.IsGenericType) { + if (type.GetGenericTypeDefinition().IsAssignableFrom(typeof(List<>))) { + foreach (var value in obj) { + Dispose(value); + } + } else if (type.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>))) { + foreach (var value in obj.Values) { + Dispose(value); + } + } + } + } + } +} +public interface IClear { + +} + +public class ClearSafeHandle: FFISafeHandle { + public ClearSafeHandle(): base() { + } + public ClearSafeHandle(IntPtr pointer): base(pointer) { + } + override protected bool ReleaseHandle() { + _UniffiHelpers.RustCall((ref RustCallStatus status) => { + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_free_clear(this.handle, ref status); + }); + return true; + } +} +public class Clear: FFIObject, IClear { + public Clear(ClearSafeHandle pointer): base(pointer) {} + public Clear() : + this( + _UniffiHelpers.RustCall( (ref RustCallStatus _status) => + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_clear_new( ref _status) +)) {} + + + + +} + +class FfiConverterTypeClear: FfiConverter { + public static FfiConverterTypeClear INSTANCE = new FfiConverterTypeClear(); + + public override ClearSafeHandle Lower(Clear value) { + return value.GetHandle(); + } + + public override Clear Lift(ClearSafeHandle value) { + return new Clear(value); + } + + public override Clear Read(BigEndianStream stream) { + return Lift(new ClearSafeHandle(new IntPtr(stream.ReadLong()))); + } + + public override int AllocationSize(Clear value) { + return 8; + } + + public override void Write(Clear value, BigEndianStream stream) { + stream.WriteLong(Lower(value).DangerousGetRawFfiValue().ToInt64()); + } +} + + + +public interface ICommand { + +} + +public class CommandSafeHandle: FFISafeHandle { + public CommandSafeHandle(): base() { + } + public CommandSafeHandle(IntPtr pointer): base(pointer) { + } + override protected bool ReleaseHandle() { + _UniffiHelpers.RustCall((ref RustCallStatus status) => { + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_free_command(this.handle, ref status); + }); + return true; + } +} +public class Command: FFIObject, ICommand { + public Command(CommandSafeHandle pointer): base(pointer) {} + + + + +} + +class FfiConverterTypeCommand: FfiConverter { + public static FfiConverterTypeCommand INSTANCE = new FfiConverterTypeCommand(); + + public override CommandSafeHandle Lower(Command value) { + return value.GetHandle(); + } + + public override Command Lift(CommandSafeHandle value) { + return new Command(value); + } + + public override Command Read(BigEndianStream stream) { + return Lift(new CommandSafeHandle(new IntPtr(stream.ReadLong()))); + } + + public override int AllocationSize(Command value) { + return 8; + } + + public override void Write(Command value, BigEndianStream stream) { + stream.WriteLong(Lower(value).DangerousGetRawFfiValue().ToInt64()); + } +} + + + +public interface IConnection { + +} + +public class ConnectionSafeHandle: FFISafeHandle { + public ConnectionSafeHandle(): base() { + } + public ConnectionSafeHandle(IntPtr pointer): base(pointer) { + } + override protected bool ReleaseHandle() { + _UniffiHelpers.RustCall((ref RustCallStatus status) => { + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_free_connection(this.handle, ref status); + }); + return true; + } +} +public class Connection: FFIObject, IConnection { + public Connection(ConnectionSafeHandle pointer): base(pointer) {} + public Connection(String @host) : + this( + _UniffiHelpers.RustCallWithError(FfiConverterTypeConnectionException.INSTANCE, (ref RustCallStatus _status) => + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_connection_new(FfiConverterString.INSTANCE.Lower(@host), ref _status) +)) {} + + + + +} + +class FfiConverterTypeConnection: FfiConverter { + public static FfiConverterTypeConnection INSTANCE = new FfiConverterTypeConnection(); + + public override ConnectionSafeHandle Lower(Connection value) { + return value.GetHandle(); + } + + public override Connection Lift(ConnectionSafeHandle value) { + return new Connection(value); + } + + public override Connection Read(BigEndianStream stream) { + return Lift(new ConnectionSafeHandle(new IntPtr(stream.ReadLong()))); + } + + public override int AllocationSize(Connection value) { + return 8; + } + + public override void Write(Connection value, BigEndianStream stream) { + stream.WriteLong(Lower(value).DangerousGetRawFfiValue().ToInt64()); + } +} + + + + + +public class ConnectionException: UniffiException { + // Each variant is a nested class + + + public class IoException : ConnectionException { + // Members + public String @error; + + // Constructor + public IoException( + String @error) { + this.@error = @error; + } + } + + + +} + +class FfiConverterTypeConnectionException : FfiConverterRustBuffer, CallStatusErrorHandler { + public static FfiConverterTypeConnectionException INSTANCE = new FfiConverterTypeConnectionException(); + + public override ConnectionException Read(BigEndianStream stream) { + var value = stream.ReadInt(); + switch (value) { + case 1: + return new ConnectionException.IoException( + FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException(String.Format("invalid error value '{0}' in FfiConverterTypeConnectionException.Read()", value)); + } + } + + public override int AllocationSize(ConnectionException value) { + switch (value) { + case ConnectionException.IoException variant_value: + return 4 + + FfiConverterString.INSTANCE.AllocationSize(variant_value.@error); + default: + throw new InternalException(String.Format("invalid error value '{0}' in FfiConverterTypeConnectionException.AllocationSize()", value)); + } + } + + public override void Write(ConnectionException value, BigEndianStream stream) { + switch (value) { + case ConnectionException.IoException variant_value: + stream.WriteInt(1); + FfiConverterString.INSTANCE.Write(variant_value.@error, stream); + break; + default: + throw new InternalException(String.Format("invalid error value '{0}' in FfiConverterTypeConnectionException.Write()", value)); + } + } +} +#pragma warning restore 8625 +public static class ServicepointBindingUniffiMethods { +} + diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/csharp.sln b/crates/servicepoint_binding_uniffi/libraries/csharp/csharp.sln new file mode 100644 index 0000000..2fbf818 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/csharp.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint", "ServicePoint\ServicePoint.csproj", "{53576D3C-E32E-49BF-BF10-2DB504E50CE1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint.Example", "ServicePoint.Example\ServicePoint.Example.csproj", "{FEF24227-090E-46C2-B8F6-ACB5AA1A4309}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint.Tests", "ServicePoint.Tests\ServicePoint.Tests.csproj", "{9DC15508-A980-4135-9FC6-659FF54B4E5C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {53576D3C-E32E-49BF-BF10-2DB504E50CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53576D3C-E32E-49BF-BF10-2DB504E50CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53576D3C-E32E-49BF-BF10-2DB504E50CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53576D3C-E32E-49BF-BF10-2DB504E50CE1}.Release|Any CPU.Build.0 = Release|Any CPU + {FEF24227-090E-46C2-B8F6-ACB5AA1A4309}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FEF24227-090E-46C2-B8F6-ACB5AA1A4309}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEF24227-090E-46C2-B8F6-ACB5AA1A4309}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FEF24227-090E-46C2-B8F6-ACB5AA1A4309}.Release|Any CPU.Build.0 = Release|Any CPU + {9DC15508-A980-4135-9FC6-659FF54B4E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9DC15508-A980-4135-9FC6-659FF54B4E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9DC15508-A980-4135-9FC6-659FF54B4E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9DC15508-A980-4135-9FC6-659FF54B4E5C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen-cs.rs b/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen-cs.rs new file mode 100644 index 0000000..ef11e75 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen-cs.rs @@ -0,0 +1,3 @@ +fn main() { + uniffi_bindgen_cs::main().unwrap(); +} diff --git a/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen.rs b/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen.rs new file mode 100644 index 0000000..f6cff6c --- /dev/null +++ b/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen.rs @@ -0,0 +1,3 @@ +fn main() { + uniffi::uniffi_bindgen_main() +} diff --git a/crates/servicepoint_binding_uniffi/src/command.rs b/crates/servicepoint_binding_uniffi/src/command.rs new file mode 100644 index 0000000..7d7906c --- /dev/null +++ b/crates/servicepoint_binding_uniffi/src/command.rs @@ -0,0 +1,20 @@ +use std::sync::Arc; + +#[uniffi::export] +trait Command: Send + Sync {} + +#[derive(uniffi::Object)] +pub struct Clear { + actual: servicepoint::Command, +} +#[uniffi::export] +impl Command for Clear {} + +#[uniffi::export] +impl Clear { + #[uniffi::constructor] + pub fn new() -> Arc { + let actual = servicepoint::Command::Clear; + Arc::new(Clear { actual }) + } +} diff --git a/crates/servicepoint_binding_uniffi/src/connection.rs b/crates/servicepoint_binding_uniffi/src/connection.rs new file mode 100644 index 0000000..340e2fe --- /dev/null +++ b/crates/servicepoint_binding_uniffi/src/connection.rs @@ -0,0 +1,23 @@ +use std::{sync::Arc}; + +#[derive(uniffi::Object)] +pub struct Connection { + actual: servicepoint::Connection, +} + +#[derive(uniffi::Error, thiserror::Error, Debug)] +pub enum ConnectionError { + #[error("An IO error occured: {error}")] + IOError { + error: String} +} + +#[uniffi::export] +impl Connection { + #[uniffi::constructor] + pub fn new(host: String) -> Result, ConnectionError> { + servicepoint::Connection::open(host) + .map(|actual|Arc::new(Connection { actual}) ) + .map_err(|err| ConnectionError::IOError { error: err.to_string()}) + } +} diff --git a/crates/servicepoint_binding_uniffi/src/lib.rs b/crates/servicepoint_binding_uniffi/src/lib.rs new file mode 100644 index 0000000..b87a67a --- /dev/null +++ b/crates/servicepoint_binding_uniffi/src/lib.rs @@ -0,0 +1,4 @@ +uniffi::setup_scaffolding!(); + +mod command; +mod connection; diff --git a/crates/servicepoint_binding_uniffi/uniffi.toml b/crates/servicepoint_binding_uniffi/uniffi.toml new file mode 100644 index 0000000..249e237 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/uniffi.toml @@ -0,0 +1,3 @@ +[bindings.csharp] +namespace = "ServicePoint" +access_modifier = "public"