send command with connection

This commit is contained in:
Vinzenz Schroeter 2024-11-03 11:12:40 +01:00
parent 07a1b8810c
commit 0dc1394935
11 changed files with 299 additions and 122 deletions

View file

@ -1,6 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set +x set -x
set -e
cargo build --release cargo build --release

View file

@ -1,5 +1,5 @@
using ServicePoint; using ServicePoint;
var connection = new Connection(""); var connection = new Connection("127.0.0.1:2342");
var clear = Command.Clear();
var clear = new Clear(); connection.Send(clear);

View file

@ -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<ServicePointException.InvalidBrightness>(() => Command.Brightness(42));
}
[Fact]
public void FadeOutSendable()
{
_connection.Send(Command.FadeOut());
}
[Fact]
public void HardResetSendable()
{
_connection.Send(Command.HardReset());
}
}

View file

@ -0,0 +1,11 @@
namespace ServicePoint.Tests;
public class ConnectionTests
{
[Fact]
public void InvalidHostnameThrows()
{
Assert.Throws<ServicePointException.IoException>(() => new Connection(""));
Assert.Throws<ServicePointException.IoException>(() => new Connection("-%6$§"));
}
}

View file

@ -0,0 +1,2 @@
global using Xunit;
global using ServicePoint;

View file

@ -1,12 +0,0 @@
using ServicePoint;
namespace ServicePoint.Tests;
public class UnitTest1
{
[Fact]
public void Test1()
{
Assert.Throws<ConnectionException.IoException>(() => new Connection(""));
}
}

View file

@ -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")] [DllImport("servicepoint_binding_uniffi")]
public static extern void uniffi_servicepoint_binding_uniffi_fn_free_command( public static extern void uniffi_servicepoint_binding_uniffi_fn_free_command(
IntPtr ptr,ref RustCallStatus _uniffi_out_err 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")] [DllImport("servicepoint_binding_uniffi")]
public static extern void uniffi_servicepoint_binding_uniffi_fn_free_connection( public static extern void uniffi_servicepoint_binding_uniffi_fn_free_connection(
IntPtr ptr,ref RustCallStatus _uniffi_out_err 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 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")] [DllImport("servicepoint_binding_uniffi")]
public static extern RustBuffer ffi_servicepoint_binding_uniffi_rustbuffer_alloc(int @size,ref RustCallStatus _uniffi_out_err 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")] [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")] [DllImport("servicepoint_binding_uniffi")]
public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new( 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")] [DllImport("servicepoint_binding_uniffi")]
public static extern uint ffi_servicepoint_binding_uniffi_uniffi_contract_version( public static extern uint ffi_servicepoint_binding_uniffi_uniffi_contract_version(
); );
@ -702,15 +737,45 @@ static class _UniFFILib {
static void uniffiCheckApiChecksums() { static void uniffiCheckApiChecksums() {
{ {
var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_clear_new(); var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_method_connection_send();
if (checksum != 31583) { if (checksum != 23796) {
throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_clear_new` checksum `31583`, library returned `{checksum}`"); 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(); var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new();
if (checksum != 63821) { if (checksum != 30445) {
throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new` checksum `63821`, library returned `{checksum}`"); 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<byte, byte> {
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<string, RustBuffer> { class FfiConverterString: FfiConverter<string, RustBuffer> {
public static FfiConverterString INSTANCE = new FfiConverterString(); 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<ClearSafeHandle>, 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<Clear, ClearSafeHandle> {
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 interface ICommand {
} }
@ -932,6 +968,36 @@ public class Command: FFIObject<CommandSafeHandle>, ICommand {
/// <exception cref="ServicePointException"></exception>
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<Command, CommandSafeHandle> { class FfiConverterTypeCommand: FfiConverter<Command, CommandSafeHandle> {
@ -962,6 +1028,9 @@ class FfiConverterTypeCommand: FfiConverter<Command, CommandSafeHandle> {
public interface IConnection { public interface IConnection {
/// <exception cref="ServicePointException"></exception>
void Send(Command @command);
} }
public class ConnectionSafeHandle: FFISafeHandle { public class ConnectionSafeHandle: FFISafeHandle {
@ -980,11 +1049,27 @@ public class Connection: FFIObject<ConnectionSafeHandle>, IConnection {
public Connection(ConnectionSafeHandle pointer): base(pointer) {} public Connection(ConnectionSafeHandle pointer): base(pointer) {}
public Connection(String @host) : public Connection(String @host) :
this( 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) _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_connection_new(FfiConverterString.INSTANCE.Lower(@host), ref _status)
)) {} )) {}
/// <exception cref="ServicePointException"></exception>
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)
));
}
} }
@ -1017,11 +1102,11 @@ class FfiConverterTypeConnection: FfiConverter<Connection, ConnectionSafeHandle>
public class ConnectionException: UniffiException { public class ServicePointException: UniffiException {
// Each variant is a nested class // Each variant is a nested class
public class IoException : ConnectionException { public class IoException : ServicePointException {
// Members // Members
public String @error; public String @error;
@ -1033,41 +1118,63 @@ public class ConnectionException: UniffiException {
} }
public class InvalidBrightness : ServicePointException {
// Members
public byte @value;
// Constructor
public InvalidBrightness(
byte @value) {
this.@value = @value;
}
}
} }
class FfiConverterTypeConnectionException : FfiConverterRustBuffer<ConnectionException>, CallStatusErrorHandler<ConnectionException> { class FfiConverterTypeServicePointException : FfiConverterRustBuffer<ServicePointException>, CallStatusErrorHandler<ServicePointException> {
public static FfiConverterTypeConnectionException INSTANCE = new FfiConverterTypeConnectionException(); public static FfiConverterTypeServicePointException INSTANCE = new FfiConverterTypeServicePointException();
public override ConnectionException Read(BigEndianStream stream) { public override ServicePointException Read(BigEndianStream stream) {
var value = stream.ReadInt(); var value = stream.ReadInt();
switch (value) { switch (value) {
case 1: case 1:
return new ConnectionException.IoException( return new ServicePointException.IoException(
FfiConverterString.INSTANCE.Read(stream)); FfiConverterString.INSTANCE.Read(stream));
case 2:
return new ServicePointException.InvalidBrightness(
FfiConverterUInt8.INSTANCE.Read(stream));
default: 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) { switch (value) {
case ConnectionException.IoException variant_value: case ServicePointException.IoException variant_value:
return 4 return 4
+ FfiConverterString.INSTANCE.AllocationSize(variant_value.@error); + FfiConverterString.INSTANCE.AllocationSize(variant_value.@error);
case ServicePointException.InvalidBrightness variant_value:
return 4
+ FfiConverterUInt8.INSTANCE.AllocationSize(variant_value.@value);
default: 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) { switch (value) {
case ConnectionException.IoException variant_value: case ServicePointException.IoException variant_value:
stream.WriteInt(1); stream.WriteInt(1);
FfiConverterString.INSTANCE.Write(variant_value.@error, stream); FfiConverterString.INSTANCE.Write(variant_value.@error, stream);
break; break;
case ServicePointException.InvalidBrightness variant_value:
stream.WriteInt(2);
FfiConverterUInt8.INSTANCE.Write(variant_value.@value, stream);
break;
default: 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));
} }
} }
} }

View file

@ -1,20 +1,37 @@
use std::sync::Arc; use std::sync::Arc;
use crate::errors::ServicePointError;
#[uniffi::export]
trait Command: Send + Sync {}
#[derive(uniffi::Object)] #[derive(uniffi::Object)]
pub struct Clear { pub struct Command {
actual: servicepoint::Command, pub(crate)actual: servicepoint::Command
}
fn actual_into_arc(actual: servicepoint::Command) -> Arc<Command> {
Arc::new(Command { actual })
} }
#[uniffi::export]
impl Command for Clear {}
#[uniffi::export] #[uniffi::export]
impl Clear { impl Command {
#[uniffi::constructor] #[uniffi::constructor]
pub fn new() -> Arc<Self> { pub fn clear() -> Arc<Self> {
let actual = servicepoint::Command::Clear; actual_into_arc(servicepoint::Command::Clear)
Arc::new(Clear { actual }) }
#[uniffi::constructor]
pub fn brightness(brightness: u8) -> Result<Arc<Self>, 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<Self> {
actual_into_arc(servicepoint::Command::FadeOut)
}
#[uniffi::constructor]
pub fn hard_reset() -> Arc<Self> {
actual_into_arc(servicepoint::Command::HardReset)
} }
} }

View file

@ -1,23 +1,29 @@
use std::{sync::Arc}; use std::sync::Arc;
use crate::command::Command;
use crate::errors::ServicePointError;
#[derive(uniffi::Object)] #[derive(uniffi::Object)]
pub struct Connection { pub struct Connection {
actual: servicepoint::Connection, actual: servicepoint::Connection,
} }
#[derive(uniffi::Error, thiserror::Error, Debug)]
pub enum ConnectionError {
#[error("An IO error occured: {error}")]
IOError {
error: String}
}
#[uniffi::export] #[uniffi::export]
impl Connection { impl Connection {
#[uniffi::constructor] #[uniffi::constructor]
pub fn new(host: String) -> Result<Arc<Self>, ConnectionError> { pub fn new(host: String) -> Result<Arc<Self>, ServicePointError> {
servicepoint::Connection::open(host) servicepoint::Connection::open(host)
.map(|actual|Arc::new(Connection { actual}) ) .map(|actual| Arc::new(Connection { actual }))
.map_err(|err| ConnectionError::IOError { error: err.to_string()}) .map_err(|err| ServicePointError::IOError { error: err.to_string() })
}
#[uniffi::constructor]
pub fn new_fake() -> Arc<Self> {
Arc::new(Self { actual: servicepoint::Connection::Fake })
}
pub fn send(&self, command: Arc<Command>) -> Result<(), ServicePointError> {
self.actual.send(command.actual.clone())
.map_err(|err| ServicePointError::IOError { error: format!("{err:?}") })
} }
} }

View file

@ -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},
}

View file

@ -2,3 +2,4 @@ uniffi::setup_scaffolding!();
mod command; mod command;
mod connection; mod connection;
mod errors;