diff --git a/Readme.md b/Readme.md index af4c9f0..a37e0ac 100644 --- a/Readme.md +++ b/Readme.md @@ -11,7 +11,7 @@ It currently supports: - Calling methods - Creating wrapper objects for DBus interfaces -- Seamlessly converting too and from D types +- Seamlessly converting to and from D types - Handling method calls and signals (includes introspection support) # Installation @@ -130,6 +130,7 @@ msg.readTuple!(typeof(args))().assertEqual(args); - `simple`: simpler wrappers around other functionality. - `conv`: low level type marshaling methods. - `util`: templates for working with D type marshaling like `canDBus!T`. +- `exception`: exception classes - `c_lib`: a D translation of the DBus C headers. Importing `ddbus` publicly imports the `thin`,`router`,`bus` and `simple` modules. @@ -169,7 +170,7 @@ It would be better to watch a file descriptor asynchronously in the event loop i `ddbus` should be complete for everyday use but is missing some fanciness that it easily could and should have: - Support for adding file descriptors to event loops like vibe.d so that it only wakes up when messages arrive and not on a timer. -- Marshaling of DBus path and file descriptor objects +- Marshaling of file descriptor objects - Better efficiency in some places, particularly the object wrapping allocates tons of delegates for every method. Pull requests are welcome, the codebase is pretty small and other than the template metaprogramming for type marshaling is fairly straightforward. diff --git a/source/ddbus/conv.d b/source/ddbus/conv.d index dab1628..d75425e 100644 --- a/source/ddbus/conv.d +++ b/source/ddbus/conv.d @@ -1,8 +1,10 @@ module ddbus.conv; import ddbus.c_lib; +import ddbus.exception : TypeMismatchException; import ddbus.util; import ddbus.thin; +import std.exception : enforce; import std.string; import std.typecons; import std.range; @@ -134,12 +136,10 @@ T readIter(T)(DBusMessageIter *iter) if (canDBus!T) { return ret; } } - static if(isTuple!T) { - assert(dbus_message_iter_get_arg_type(iter) == 'r'); - } else static if(is(T == DictionaryEntry!(K1, V1), K1, V1)) { - assert(dbus_message_iter_get_arg_type(iter) == 'e'); - } else static if(!is(T == DBusAny) && !is(T == Variant!DBusAny)) { - assert(dbus_message_iter_get_arg_type(iter) == typeCode!T()); + static if(!is(T == DBusAny) && !is(T == Variant!DBusAny)) { + auto argType = dbus_message_iter_get_arg_type(iter); + enforce(argType == typeCode!T(), + new TypeMismatchException(argType, typeCode!T())); } static if(is(T==string) || is(T==ObjectPath)) { const(char)* cStr; @@ -272,6 +272,11 @@ unittest { auto args = tuple(5,true,"wow",var(5.9),[6,5],tuple(6.2,4,[["lol"]],emptyB,var([4,2])),map,anyVar,complexVar); msg.build(args.expand); msg.signature().assertEqual("ibsvai(diaasabv)a{ss}tv"); + + msg.read!string().assertThrow!TypeMismatchException(); + msg.readTuple!(Tuple!(int, bool, double)).assertThrow!TypeMismatchException(); + msg.readTuple!(Tuple!(int, bool, string, double)).assertEqual(tuple(5,true,"wow", 5.9)); + msg.readTuple!(typeof(args))().assertEqual(args); DBusMessageIter iter; dbus_message_iter_init(msg.msg, &iter); diff --git a/source/ddbus/exception.d b/source/ddbus/exception.d new file mode 100644 index 0000000..7e80bc1 --- /dev/null +++ b/source/ddbus/exception.d @@ -0,0 +1,66 @@ +module ddbus.exception; + +import ddbus.c_lib; + +package T wrapErrors(T)( + T delegate(DBusError *err) del, + string file = __FILE__, + size_t line = __LINE__, + Throwable next = null +) { + DBusError error; + dbus_error_init(&error); + T ret = del(&error); + if(dbus_error_is_set(&error)) { + auto ex = new DBusException(&error, file, line, next); + dbus_error_free(&error); + throw ex; + } + return ret; +} + +/++ + Thrown when a DBus error code was returned by libdbus. ++/ +class DBusException : Exception { + private this( + scope DBusError *err, + string file = __FILE__, + size_t line = __LINE__, + Throwable next = null + ) pure nothrow { + import std.string : fromStringz; + + super(err.message.fromStringz().idup, file, line, next); + } +} + +/++ + Thrown when the signature of a message does not match the requested types. ++/ +class TypeMismatchException : Exception { + package this( + int expectedType, + int actualType, + string file = __FILE__, + size_t line = __LINE__, + Throwable next = null + ) pure nothrow @safe { + _expectedType = expectedType; + _actualType = actualType; + super("The type of value at the current position in the message does not match the type of value to be read." + ~ " Expected: " ~ cast(char) expectedType ~ ", Got: " ~ cast(char) actualType); + } + + int expectedType() @property pure const nothrow @nogc { + return _expectedType; + } + + int actualType() @property pure const nothrow @nogc { + return _actualType; + } + + private: + int _expectedType; + int _actualType; +} diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 3268c6b..de60eba 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -12,23 +12,8 @@ import std.conv; import std.range; import std.algorithm; -class DBusException : Exception { - this(DBusError *err) { - super(err.message.fromStringz().idup); - } -} - -T wrapErrors(T)(T delegate(DBusError *err) del) { - DBusError error; - dbus_error_init(&error); - T ret = del(&error); - if(dbus_error_is_set(&error)) { - auto ex = new DBusException(&error); - dbus_error_free(&error); - throw ex; - } - return ret; -} +// This import is public for backwards compatibility +public import ddbus.exception : wrapErrors, DBusException; struct ObjectPath { private string _value;