diff --git a/crates/servicepoint_binding_uniffi/generate-bindings.sh b/crates/servicepoint_binding_uniffi/generate-bindings.sh index 97ad72e..539b61e 100755 --- a/crates/servicepoint_binding_uniffi/generate-bindings.sh +++ b/crates/servicepoint_binding_uniffi/generate-bindings.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash -set +x +set -x +set -e cargo build --release diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/Program.cs b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/Program.cs index edca01c..ad4a868 100644 --- a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/Program.cs +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/Program.cs @@ -1,5 +1,5 @@ using ServicePoint; -var connection = new Connection(""); - -var clear = new Clear(); +var connection = new Connection("127.0.0.1:2342"); +var clear = Command.Clear(); +connection.Send(clear); diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/CommandTests.cs b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/CommandTests.cs new file mode 100644 index 0000000..31cb52b --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/CommandTests.cs @@ -0,0 +1,36 @@ +namespace ServicePoint.Tests; + +public class CommandTests +{ + private Connection _connection = Connection.NewFake(); + + [Fact] + public void ClearSendable() + { + _connection.Send(Command.Clear()); + } + + [Fact] + public void BrightnessSendable() + { + _connection.Send(Command.Brightness(5)); + } + + [Fact] + public void InvalidBrightnessThrows() + { + Assert.Throws(() => Command.Brightness(42)); + } + + [Fact] + public void FadeOutSendable() + { + _connection.Send(Command.FadeOut()); + } + + [Fact] + public void HardResetSendable() + { + _connection.Send(Command.HardReset()); + } +} diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/ConnectionTests.cs b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/ConnectionTests.cs new file mode 100644 index 0000000..be0000f --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/ConnectionTests.cs @@ -0,0 +1,11 @@ +namespace ServicePoint.Tests; + +public class ConnectionTests +{ + [Fact] + public void InvalidHostnameThrows() + { + Assert.Throws(() => new Connection("")); + Assert.Throws(() => new Connection("-%6$§")); + } +} diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/GlobalUsings.cs b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/GlobalUsings.cs new file mode 100644 index 0000000..a09810b --- /dev/null +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Xunit; +global using ServicePoint; diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/UnitTest1.cs b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/UnitTest1.cs deleted file mode 100644 index 46991d7..0000000 --- a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/UnitTest1.cs +++ /dev/null @@ -1,12 +0,0 @@ -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_binding_uniffi.cs b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/servicepoint_binding_uniffi.cs index 5e90f6c..6285122 100644 --- a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/servicepoint_binding_uniffi.cs +++ b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/servicepoint_binding_uniffi.cs @@ -428,20 +428,27 @@ static class _UniFFILib { } - [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 CommandSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_command_brightness(byte @brightness,ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern CommandSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_command_clear(ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern CommandSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_command_fade_out(ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern CommandSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset(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 @@ -451,6 +458,14 @@ static class _UniFFILib { 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 ConnectionSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_connection_new_fake(ref RustCallStatus _uniffi_out_err + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern void uniffi_servicepoint_binding_uniffi_fn_method_connection_send(ConnectionSafeHandle @ptr,CommandSafeHandle @command,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 ); @@ -680,13 +695,33 @@ static class _UniFFILib { ); [DllImport("servicepoint_binding_uniffi")] - public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_clear_new( + public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_method_connection_send( + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_command_brightness( + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_command_clear( + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_command_fade_out( + ); + + [DllImport("servicepoint_binding_uniffi")] + public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset( ); [DllImport("servicepoint_binding_uniffi")] public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new( ); + [DllImport("servicepoint_binding_uniffi")] + public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new_fake( + ); + [DllImport("servicepoint_binding_uniffi")] public static extern uint ffi_servicepoint_binding_uniffi_uniffi_contract_version( ); @@ -702,15 +737,45 @@ static class _UniFFILib { 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_method_connection_send(); + if (checksum != 23796) { + throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_method_connection_send` checksum `23796`, library returned `{checksum}`"); + } + } + { + var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_command_brightness(); + if (checksum != 11291) { + throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_command_brightness` checksum `11291`, library returned `{checksum}`"); + } + } + { + var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_command_clear(); + if (checksum != 11035) { + throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_command_clear` checksum `11035`, library returned `{checksum}`"); + } + } + { + var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_command_fade_out(); + if (checksum != 49231) { + throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_command_fade_out` checksum `49231`, library returned `{checksum}`"); + } + } + { + var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset(); + if (checksum != 62130) { + throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset` checksum `62130`, 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}`"); + if (checksum != 30445) { + throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new` checksum `30445`, library returned `{checksum}`"); + } + } + { + var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new_fake(); + if (checksum != 54331) { + throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new_fake` checksum `54331`, library returned `{checksum}`"); } } } @@ -723,6 +788,32 @@ static class _UniFFILib { +class FfiConverterUInt8: FfiConverter { + public static FfiConverterUInt8 INSTANCE = new FfiConverterUInt8(); + + public override byte Lift(byte value) { + return value; + } + + public override byte Read(BigEndianStream stream) { + return stream.ReadByte(); + } + + public override byte Lower(byte value) { + return value; + } + + public override int AllocationSize(byte value) { + return 1; + } + + public override void Write(byte value, BigEndianStream stream) { + stream.WriteByte(value); + } +} + + + class FfiConverterString: FfiConverter { public static FfiConverterString INSTANCE = new FfiConverterString(); @@ -855,61 +946,6 @@ static class FFIObjectUtil { } } } -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 { } @@ -932,6 +968,36 @@ public class Command: FFIObject, ICommand { + /// + public static Command Brightness(byte @brightness) { + return new Command( + _UniffiHelpers.RustCallWithError(FfiConverterTypeServicePointException.INSTANCE, (ref RustCallStatus _status) => + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_command_brightness(FfiConverterUInt8.INSTANCE.Lower(@brightness), ref _status) +)); + } + + public static Command Clear() { + return new Command( + _UniffiHelpers.RustCall( (ref RustCallStatus _status) => + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_command_clear( ref _status) +)); + } + + public static Command FadeOut() { + return new Command( + _UniffiHelpers.RustCall( (ref RustCallStatus _status) => + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_command_fade_out( ref _status) +)); + } + + public static Command HardReset() { + return new Command( + _UniffiHelpers.RustCall( (ref RustCallStatus _status) => + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset( ref _status) +)); + } + + } class FfiConverterTypeCommand: FfiConverter { @@ -962,6 +1028,9 @@ class FfiConverterTypeCommand: FfiConverter { public interface IConnection { + /// + void Send(Command @command); + } public class ConnectionSafeHandle: FFISafeHandle { @@ -980,13 +1049,29 @@ public class Connection: FFIObject, IConnection { public Connection(ConnectionSafeHandle pointer): base(pointer) {} public Connection(String @host) : this( - _UniffiHelpers.RustCallWithError(FfiConverterTypeConnectionException.INSTANCE, (ref RustCallStatus _status) => + _UniffiHelpers.RustCallWithError(FfiConverterTypeServicePointException.INSTANCE, (ref RustCallStatus _status) => _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_connection_new(FfiConverterString.INSTANCE.Lower(@host), ref _status) )) {} + /// + public void Send(Command @command) { + _UniffiHelpers.RustCallWithError(FfiConverterTypeServicePointException.INSTANCE, (ref RustCallStatus _status) => + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_method_connection_send(this.GetHandle(), FfiConverterTypeCommand.INSTANCE.Lower(@command), ref _status) +); + } + + + public static Connection NewFake() { + return new Connection( + _UniffiHelpers.RustCall( (ref RustCallStatus _status) => + _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_connection_new_fake( ref _status) +)); + } + + } class FfiConverterTypeConnection: FfiConverter { @@ -1017,11 +1102,11 @@ class FfiConverterTypeConnection: FfiConverter -public class ConnectionException: UniffiException { +public class ServicePointException: UniffiException { // Each variant is a nested class - public class IoException : ConnectionException { + public class IoException : ServicePointException { // Members public String @error; @@ -1032,42 +1117,64 @@ public class ConnectionException: UniffiException { } } + + public class InvalidBrightness : ServicePointException { + // Members + public byte @value; + + // Constructor + public InvalidBrightness( + byte @value) { + this.@value = @value; + } + } + } -class FfiConverterTypeConnectionException : FfiConverterRustBuffer, CallStatusErrorHandler { - public static FfiConverterTypeConnectionException INSTANCE = new FfiConverterTypeConnectionException(); +class FfiConverterTypeServicePointException : FfiConverterRustBuffer, CallStatusErrorHandler { + public static FfiConverterTypeServicePointException INSTANCE = new FfiConverterTypeServicePointException(); - public override ConnectionException Read(BigEndianStream stream) { + public override ServicePointException Read(BigEndianStream stream) { var value = stream.ReadInt(); switch (value) { case 1: - return new ConnectionException.IoException( + return new ServicePointException.IoException( FfiConverterString.INSTANCE.Read(stream)); + case 2: + return new ServicePointException.InvalidBrightness( + FfiConverterUInt8.INSTANCE.Read(stream)); default: - throw new InternalException(String.Format("invalid error value '{0}' in FfiConverterTypeConnectionException.Read()", value)); + throw new InternalException(String.Format("invalid error value '{0}' in FfiConverterTypeServicePointException.Read()", value)); } } - public override int AllocationSize(ConnectionException value) { + public override int AllocationSize(ServicePointException value) { switch (value) { - case ConnectionException.IoException variant_value: + case ServicePointException.IoException variant_value: return 4 + FfiConverterString.INSTANCE.AllocationSize(variant_value.@error); + case ServicePointException.InvalidBrightness variant_value: + return 4 + + FfiConverterUInt8.INSTANCE.AllocationSize(variant_value.@value); default: - throw new InternalException(String.Format("invalid error value '{0}' in FfiConverterTypeConnectionException.AllocationSize()", value)); + throw new InternalException(String.Format("invalid error value '{0}' in FfiConverterTypeServicePointException.AllocationSize()", value)); } } - public override void Write(ConnectionException value, BigEndianStream stream) { + public override void Write(ServicePointException value, BigEndianStream stream) { switch (value) { - case ConnectionException.IoException variant_value: + case ServicePointException.IoException variant_value: stream.WriteInt(1); FfiConverterString.INSTANCE.Write(variant_value.@error, stream); break; + case ServicePointException.InvalidBrightness variant_value: + stream.WriteInt(2); + FfiConverterUInt8.INSTANCE.Write(variant_value.@value, stream); + break; default: - throw new InternalException(String.Format("invalid error value '{0}' in FfiConverterTypeConnectionException.Write()", value)); + throw new InternalException(String.Format("invalid error value '{0}' in FfiConverterTypeServicePointException.Write()", value)); } } } diff --git a/crates/servicepoint_binding_uniffi/src/command.rs b/crates/servicepoint_binding_uniffi/src/command.rs index 7d7906c..81562a7 100644 --- a/crates/servicepoint_binding_uniffi/src/command.rs +++ b/crates/servicepoint_binding_uniffi/src/command.rs @@ -1,20 +1,37 @@ use std::sync::Arc; - -#[uniffi::export] -trait Command: Send + Sync {} +use crate::errors::ServicePointError; #[derive(uniffi::Object)] -pub struct Clear { - actual: servicepoint::Command, +pub struct Command { + pub(crate)actual: servicepoint::Command +} + +fn actual_into_arc(actual: servicepoint::Command) -> Arc { + Arc::new(Command { actual }) } -#[uniffi::export] -impl Command for Clear {} #[uniffi::export] -impl Clear { +impl Command { #[uniffi::constructor] - pub fn new() -> Arc { - let actual = servicepoint::Command::Clear; - Arc::new(Clear { actual }) + pub fn clear() -> Arc { + actual_into_arc(servicepoint::Command::Clear) + } + + #[uniffi::constructor] + pub fn brightness(brightness: u8) -> Result, ServicePointError> { + servicepoint::Brightness::try_from(brightness) + .map_err(move |value| ServicePointError::InvalidBrightness{value}) + .map(servicepoint::Command::Brightness) + .map(actual_into_arc) + } + + #[uniffi::constructor] + pub fn fade_out() -> Arc { + actual_into_arc(servicepoint::Command::FadeOut) + } + + #[uniffi::constructor] + pub fn hard_reset() -> Arc { + actual_into_arc(servicepoint::Command::HardReset) } } diff --git a/crates/servicepoint_binding_uniffi/src/connection.rs b/crates/servicepoint_binding_uniffi/src/connection.rs index 340e2fe..4e7ed79 100644 --- a/crates/servicepoint_binding_uniffi/src/connection.rs +++ b/crates/servicepoint_binding_uniffi/src/connection.rs @@ -1,23 +1,29 @@ -use std::{sync::Arc}; +use std::sync::Arc; + +use crate::command::Command; +use crate::errors::ServicePointError; #[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> { + pub fn new(host: String) -> Result, ServicePointError> { servicepoint::Connection::open(host) - .map(|actual|Arc::new(Connection { actual}) ) - .map_err(|err| ConnectionError::IOError { error: err.to_string()}) + .map(|actual| Arc::new(Connection { actual })) + .map_err(|err| ServicePointError::IOError { error: err.to_string() }) + } + + #[uniffi::constructor] + pub fn new_fake() -> Arc { + Arc::new(Self { actual: servicepoint::Connection::Fake }) + } + + pub fn send(&self, command: Arc) -> Result<(), ServicePointError> { + self.actual.send(command.actual.clone()) + .map_err(|err| ServicePointError::IOError { error: format!("{err:?}") }) } } diff --git a/crates/servicepoint_binding_uniffi/src/errors.rs b/crates/servicepoint_binding_uniffi/src/errors.rs new file mode 100644 index 0000000..bb41d81 --- /dev/null +++ b/crates/servicepoint_binding_uniffi/src/errors.rs @@ -0,0 +1,8 @@ + +#[derive(uniffi::Error, thiserror::Error, Debug)] +pub enum ServicePointError { + #[error("An IO error occurred: {error}")] + IOError {error: String}, + #[error("The specified brightness value {value} is out of range")] + InvalidBrightness {value:u8}, +} \ No newline at end of file diff --git a/crates/servicepoint_binding_uniffi/src/lib.rs b/crates/servicepoint_binding_uniffi/src/lib.rs index b87a67a..b86bdaa 100644 --- a/crates/servicepoint_binding_uniffi/src/lib.rs +++ b/crates/servicepoint_binding_uniffi/src/lib.rs @@ -2,3 +2,4 @@ uniffi::setup_scaffolding!(); mod command; mod connection; +mod errors;