Merge branch 'master' into replace-body-with-do

This commit is contained in:
Jan Jurzitza 2018-03-19 09:04:33 +01:00 committed by GitHub
commit 100ce3e60d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 928 additions and 629 deletions

8
.editorconfig Normal file
View file

@ -0,0 +1,8 @@
[**.d]
indent_style = space
indent_size = 2
dfmt_brace_style = otbs
dfmt_space_after_keywords = true
dfmt_space_after_cast = true
dfmt_template_constraint_style = always_newline_indent

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ __dummy.html
libddbus.* libddbus.*
__test__library__ __test__library__
/ddbus-test-* /ddbus-test-*
*.lst

View file

@ -21,7 +21,7 @@ Before using, you will need to have the DBus C library installed on your compute
`ddbus` is available on [DUB](http://code.dlang.org/packages/ddbus) so you can simply include it in your `dub.json`: `ddbus` is available on [DUB](http://code.dlang.org/packages/ddbus) so you can simply include it in your `dub.json`:
```json ```json
"dependencies": { "dependencies": {
"ddbus": "~>2.1.0" "ddbus": "~>2.3.0"
} }
``` ```
@ -108,37 +108,92 @@ See the Concurrent Updates section for details how to implement this in a custom
## Type Marshaling ## Type Marshaling
`ddbus` includes fancy templated methods for marshaling D types in and out of DBus messages. `ddbus` includes fancy templated methods for marshaling D types in and out of DBus messages.
All DBus-compatible basic types work (except dbus path objects and file descriptors). All DBus-compatible basic types work (except file descriptors).
Any forward range can be marshalled in as DBus array of that type but arrays must be taken out as dynamic arrays. Any forward range can be marshaled in as DBus array of that type but arrays must be taken out as dynamic arrays.
Structures are mapped to `Tuple` from `std.typecons`.
As per version 2.3.0, D `struct` types are fully supported by `ddbus`. By default all public fields of a structure are marshaled. This behavior can be [changed by UDAs](#customizing-marshaling-of-struct-types). Mapping DBus structures to a matching instance of `std.typecons.Tuple`, like earlier versions of `ddbus` did, is also still supported.
Example using the lower level interface, the simple interfaces use these behind the scenes: Example using the lower level interface, the simple interfaces use these behind the scenes:
```d ```d
Message msg = Message("org.example.wow","/wut","org.test.iface","meth"); Message msg = Message("org.example.wow","/wut","org.test.iface","meth");
bool[] emptyB;
auto args = tuple(5,true,"wow",[6,5],tuple(6.2,4,[["lol"]],emptyB)); struct S {
double a;
int b;
string[][] c;
bool[] d;
}
auto s = S(6.2, 4, [["lol"]], []);
auto args = tuple(5, true, "wow", [6, 5], s);
msg.build(args.expand); msg.build(args.expand);
msg.signature().assertEqual("ibsai(diaasab)"); msg.signature().assertEqual("ibsai(diaasab)");
msg.readTuple!(typeof(args))().assertEqual(args); msg.readTuple!(typeof(args))().assertEqual(args);
``` ```
### Basic types
These are the basic types supported by `ddbus`:
`bool`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `double`, `string`, `ObjectPath`
### Overview of mappings of other types:
| D type | DBus type | Comments
| -------------------------------------------- | ------------------------ | ---
| any `enum` | `enum` base type | Only the exact values present in the definition of the `enum` type will be allowed.
| `std.typecons.BitFlags` | `enum` base type | Allows usage of OR'ed values of a flags `enum`.
| dynamic array `T[]` | array |
| associative array `V[K]` | array of key-value pairs | DBus has a special type for key-value pairs, which can be used as the element type of an array only.
| `Tuple!(T...)` | structure | The DBus structure will map all of the `Tuple`'s values in sequence.
| any `struct` | structure | The DBus structure will map all public fields of the `struct` type in order of definition, unless otherwise specified using UDAs.
| `ddbus` style variant `Variant!T` | variant | `Variant!T` is in fact just a wrapper type to force representation as a variant in DBus, use `Variant!DBusAny` for actual dynamic typing.
| Phobos style variants `std.variant.VariantN` | variant | Only supported if set of allowed types is limited to types that can be marshaled by `ddbus`, so `std.variant.Variant` is not supported, but `std.variant.Algebraic` may be, depending on allowed types
### Customizing marshaling of `struct` types
Marshaling behavior can be changed for a `struct` type by adding the `@dbusMarshaling`
UDA with the appropriate flag. The following flags are supported:
- `includePrivateFields` enables marshaling of private fields
- `manualOnly` disables marshaling of all fields
Marshaling of individual fields can be enabled or disabled by setting the `DBusMarshal`
flag as an UDA. I.e. `@Yes.DBusMarshal` or `@No.DBusMarshal`.
Note: symbols `Yes` and `No` are defined in `std.typecons`.
After converting a DBus structure to a D `struct`, any fields that are not marshaled
will appear freshly initialized. This is true even when just converting a `struct` to
`DBusAny` and back.
```d
import ddbus.attributes;
import std.typecons;
@dbusMarshaling(MarshalingFlag.includePrivateFields)
struct WeirdThing {
int a; // marshaled (default behavior not overridden)
@No.DBusMarshal int b; // explicitly not marshaled
private int c; // marshaled, because of includePrivateFields
}
```
## Modules ## Modules
- `thin`: thin wrapper types - `attributes`: defines some UDAs (and related templates) that can be used to customize
- `router`: message and signal routing based on `MessagePattern` structs. struct marshaling.
- `bus`: bus functionality like requesting names and event loops. - `bus`: bus functionality like requesting names and event loops.
- `simple`: simpler wrappers around other functionality.
- `conv`: low level type marshaling methods. - `conv`: low level type marshaling methods.
- `util`: templates for working with D type marshaling like `canDBus!T`.
- `exception`: exception classes - `exception`: exception classes
- `c_lib`: a D translation of the DBus C headers. - `router`: message and signal routing based on `MessagePattern` structs.
- `simple`: simpler wrappers around other functionality.
- `thin`: thin wrapper types
- `util`: templates for working with D type marshaling like `canDBus!T`.
- `c_lib`: a D translation of the DBus C headers
(you generally should not need to use these directly).
Importing `ddbus` publicly imports the `thin`,`router`,`bus` and `simple` modules. Importing `ddbus` will publicly import the `thin`, `router`, `bus`, `simple` and
These provide most of the functionality you probably want, `attributes` modules. These provide most of the functionality you probably want,
you can import the others if you want lower level control. you can import the others if you want lower level control.
Nothing is hidden so if `ddbus` doesn't provide something you can simply import `c_lib` and use the pointers Nothing is hidden so if `ddbus` doesn't provide something, you can always import
contained in the thin wrapper structs to do it yourself. `c_lib` and use the pointers contained in the thin wrapper structs to do it yourself.
# Concurrent Updates # Concurrent Updates

1
examples/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
dub.selections.json

1
examples/client/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
client

6
examples/client/dub.json Normal file
View file

@ -0,0 +1,6 @@
{
"name": "client",
"dependencies": {
"ddbus": {"path": "../../"}
}
}

View file

@ -0,0 +1,22 @@
import core.time;
import std.stdio;
import ddbus;
void testCall(Connection conn) {
for(int i = 0; i < 50; i++) {
Message msg = Message("ca.thume.transience","/ca/thume/transience/screensurface",
"ca.thume.transience.screensurface","testDot");
conn.sendBlocking(msg);
}
Message msg2 = Message("ca.thume.transience","/ca/thume/transience/screensurface",
"ca.thume.transience.screensurface","testPing");
Message res = conn.sendWithReplyBlocking(msg2, 3.seconds);
int result = res.read!int();
writeln(result);
}
void main() {
Connection conn = connectToBus();
testCall(conn);
writeln("It worked!");
}

1
examples/server/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
server

6
examples/server/dub.json Normal file
View file

@ -0,0 +1,6 @@
{
"name": "server",
"dependencies": {
"ddbus": {"path": "../../"}
}
}

View file

@ -1,19 +1,6 @@
import std.stdio; import std.stdio;
import ddbus; import ddbus;
void testCall(Connection conn) {
for(int i = 0; i < 50; i++) {
Message msg = Message("ca.thume.transience","/ca/thume/transience/screensurface",
"ca.thume.transience.screensurface","testDot");
conn.sendBlocking(msg);
}
Message msg2 = Message("ca.thume.transience","/ca/thume/transience/screensurface",
"ca.thume.transience.screensurface","testPing");
Message res = conn.sendWithReplyBlocking(msg2,3000);
int result = res.read!int();
writeln(result);
}
void testServe(Connection conn) { void testServe(Connection conn) {
auto router = new MessageRouter(); auto router = new MessageRouter();
MessagePattern patt = MessagePattern("/root","ca.thume.test","test"); MessagePattern patt = MessagePattern("/root","ca.thume.test","test");
@ -35,5 +22,5 @@ void testServe(Connection conn) {
void main() { void main() {
Connection conn = connectToBus(); Connection conn = connectToBus();
testServe(conn); testServe(conn);
writeln("It worked!"); writeln("It worked!");
} }

View file

@ -10,17 +10,23 @@ import std.typecons : BitFlags, Flag;
Default is to include public fields only Default is to include public fields only
+/ +/
enum MarshalingFlag : ubyte { enum MarshalingFlag : ubyte {
includePrivateFields = 1 << 0, /// Automatically include private fields /++
manualOnly = 1 << 7 /// Only include fields with explicit Automatically include private fields
/// `@Yes.DBusMarshal`. This overrides any +/
/// `include` flags. includePrivateFields = 1 << 0,
/++
Only include fields with explicit `@Yes.DBusMarshal`. This overrides any
`include` flags.
+/
manualOnly = 1 << 7
} }
/++ /++
UDA for specifying DBus marshaling options on structs UDA for specifying DBus marshaling options on structs
+/ +/
auto dbusMarshaling(Args)(Args args ...) auto dbusMarshaling(Args)(Args args...)
if (allSatisfy!(isMarshalingFlag, Args)) { if (allSatisfy!(isMarshalingFlag, Args)) {
return BitFlags!MarshalingFlag(args); return BitFlags!MarshalingFlag(args);
} }
@ -29,37 +35,40 @@ package(ddbus) template isAllowedField(alias field) {
private alias getUDAs!(field, Flag!"DBusMarshal") UDAs; private alias getUDAs!(field, Flag!"DBusMarshal") UDAs;
static if (UDAs.length != 0) { static if (UDAs.length != 0) {
static assert (UDAs.length == 1, static assert(UDAs.length == 1,
"Only one UDA of type Flag!\"DBusMarshal\" allowed on struct field."); "Only one UDA of type Flag!\"DBusMarshal\" allowed on struct field.");
static assert (is(typeof(UDAs[0]) == Flag!"DBusMarshal"),
"Did you intend to add UDA Yes.DBusMarshal or No.DBusMarshal?"); static assert(is(typeof(UDAs[0]) == Flag!"DBusMarshal"),
"Did you intend to add UDA Yes.DBusMarshal or No.DBusMarshal?");
enum isAllowedField = cast(bool) UDAs[0]; enum isAllowedField = cast(bool) UDAs[0];
} else static if (!(flags & MarshalingFlag.manualOnly)) { } else static if (!(flags & MarshalingFlag.manualOnly)) {
static if (__traits(getProtection, field) == "public") static if (__traits(getProtection, field) == "public") {
enum isAllowedField = true; enum isAllowedField = true;
else static if (cast(bool) (flags & MarshalingFlag.includePrivateFields)) } else static if (cast(bool)(flags & MarshalingFlag.includePrivateFields)) {
enum isAllowedField = true; enum isAllowedField = true;
else } else {
enum isAllowedField = false; enum isAllowedField = false;
} else }
} else {
enum isAllowedField = false; enum isAllowedField = false;
}
} }
private template isMarshalingFlag(T) { private template isMarshalingFlag(T) {
enum isMarshalingFlag = is(T == MarshalingFlag); enum isMarshalingFlag = is(T == MarshalingFlag);
} }
private template marshalingFlags(S) if (is(S == struct)) { private template marshalingFlags(S)
if (is(S == struct)) {
private alias getUDAs!(S, BitFlags!MarshalingFlag) UDAs; private alias getUDAs!(S, BitFlags!MarshalingFlag) UDAs;
static if (UDAs.length == 0) static if (UDAs.length == 0) {
enum marshalingFlags = BitFlags!MarshalingFlag.init; enum marshalingFlags = BitFlags!MarshalingFlag.init;
else { } else {
static assert (UDAs.length == 1, static assert(UDAs.length == 1, "Only one @dbusMarshaling UDA allowed on type.");
"Only one @dbusMarshaling UDA allowed on type."); static assert(is(typeof(UDAs[0]) == BitFlags!MarshalingFlag),
static assert (is(typeof(UDAs[0]) == BitFlags!MarshalingFlag), "Huh? Did you intend to use @dbusMarshaling UDA?");
"Huh? Did you intend to use @dbusMarshaling UDA?");
enum marshalingFlags = UDAs[0]; enum marshalingFlags = UDAs[0];
} }
} }

View file

@ -10,16 +10,18 @@ enum BusPath = "/org/freedesktop/DBus";
enum BusInterface = "org.freedesktop.DBus"; enum BusInterface = "org.freedesktop.DBus";
enum NameFlags { enum NameFlags {
AllowReplace = 1, ReplaceExisting = 2, NoQueue = 4 AllowReplace = 1,
ReplaceExisting = 2,
NoQueue = 4
} }
/// Requests a DBus well-known name. /// Requests a DBus well-known name.
/// returns if the name is owned after the call. /// returns if the name is owned after the call.
/// Involves blocking call on a DBus method, may throw an exception on failure. /// Involves blocking call on a DBus method, may throw an exception on failure.
bool requestName(Connection conn, string name, bool requestName(Connection conn, string name,
NameFlags flags = NameFlags.NoQueue | NameFlags.AllowReplace) { NameFlags flags = NameFlags.NoQueue | NameFlags.AllowReplace) {
auto msg = Message(BusService,BusPath,BusInterface,"RequestName"); auto msg = Message(BusService, BusPath, BusInterface, "RequestName");
msg.build(name,cast(uint)(flags)); msg.build(name, cast(uint)(flags));
auto res = conn.sendWithReplyBlocking(msg).to!uint; auto res = conn.sendWithReplyBlocking(msg).to!uint;
return (res == 1) || (res == 4); return (res == 1) || (res == 4);
} }
@ -28,7 +30,8 @@ bool requestName(Connection conn, string name,
/// and isn't guaranteed to work with other tasks and threads. /// and isn't guaranteed to work with other tasks and threads.
/// Use only for apps that only do DBus triggered things. /// Use only for apps that only do DBus triggered things.
void simpleMainLoop(Connection conn) { void simpleMainLoop(Connection conn) {
while(dbus_connection_read_write_dispatch(conn.conn, -1)) {} // empty loop body while (dbus_connection_read_write_dispatch(conn.conn, -1)) {
} // empty loop body
} }
/// Single tick in the DBus connection which can be used for /// Single tick in the DBus connection which can be used for
@ -39,7 +42,7 @@ bool tick(Connection conn) {
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
Connection conn = connectToBus(); Connection conn = connectToBus();
conn.requestName("ca.thume.ddbus.testing").assertTrue(); conn.requestName("ca.thume.ddbus.testing").assertTrue();
} }

View file

@ -1,6 +1,10 @@
module ddbus.c_lib; module ddbus.c_lib;
import core.stdc.config; import core.stdc.config;
import core.stdc.stdarg; import core.stdc.stdarg;
// dfmt off
extern (C): extern (C):
// START dbus/dbus-arch-deps.d // START dbus/dbus-arch-deps.d
alias c_long dbus_int64_t; alias c_long dbus_int64_t;

View file

@ -7,36 +7,37 @@ import ddbus.util;
import ddbus.thin; import ddbus.thin;
import std.exception : enforce; import std.exception : enforce;
import std.meta: allSatisfy; import std.meta : allSatisfy;
import std.string; import std.string;
import std.typecons; import std.typecons;
import std.range; import std.range;
import std.traits; import std.traits;
import std.variant : VariantN; import std.variant : VariantN;
void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) { void buildIter(TS...)(DBusMessageIter* iter, TS args)
foreach(index, arg; args) { if (allCanDBus!TS) {
foreach (index, arg; args) {
alias TS[index] T; alias TS[index] T;
static if(is(T == string)) { static if (is(T == string)) {
immutable(char)* cStr = arg.toStringz(); immutable(char)* cStr = arg.toStringz();
dbus_message_iter_append_basic(iter,typeCode!T,&cStr); dbus_message_iter_append_basic(iter, typeCode!T, &cStr);
} else static if(is(T == ObjectPath)) { } else static if (is(T == ObjectPath)) {
immutable(char)* cStr = arg.toString().toStringz(); immutable(char)* cStr = arg.toString().toStringz();
dbus_message_iter_append_basic(iter,typeCode!T,&cStr); dbus_message_iter_append_basic(iter, typeCode!T, &cStr);
} else static if(is(T==bool)) { } else static if (is(T == bool)) {
dbus_bool_t longerBool = arg; // dbus bools are ints dbus_bool_t longerBool = arg; // dbus bools are ints
dbus_message_iter_append_basic(iter,typeCode!T,&longerBool); dbus_message_iter_append_basic(iter, typeCode!T, &longerBool);
} else static if(isTuple!T) { } else static if (isTuple!T) {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_open_container(iter, 'r', null, &sub); dbus_message_iter_open_container(iter, 'r', null, &sub);
buildIter(&sub, arg.expand); buildIter(&sub, arg.expand);
dbus_message_iter_close_container(iter, &sub); dbus_message_iter_close_container(iter, &sub);
} else static if(isInputRange!T) { } else static if (isInputRange!T) {
DBusMessageIter sub; DBusMessageIter sub;
const(char)* subSig = (typeSig!(ElementType!T)()).toStringz(); const(char)* subSig = (typeSig!(ElementType!T)()).toStringz();
dbus_message_iter_open_container(iter, 'a', subSig, &sub); dbus_message_iter_open_container(iter, 'a', subSig, &sub);
foreach(x; arg) { foreach (x; arg) {
static if(isInstanceOf!(DictionaryEntry, typeof(x))) { static if (isInstanceOf!(DictionaryEntry, typeof(x))) {
DBusMessageIter entry; DBusMessageIter entry;
dbus_message_iter_open_container(&sub, 'e', null, &entry); dbus_message_iter_open_container(&sub, 'e', null, &entry);
buildIter(&entry, x.key); buildIter(&entry, x.key);
@ -47,11 +48,11 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
} }
} }
dbus_message_iter_close_container(iter, &sub); dbus_message_iter_close_container(iter, &sub);
} else static if(isAssociativeArray!T) { } else static if (isAssociativeArray!T) {
DBusMessageIter sub; DBusMessageIter sub;
const(char)* subSig = typeSig!T[1..$].toStringz(); const(char)* subSig = typeSig!T[1 .. $].toStringz();
dbus_message_iter_open_container(iter, 'a', subSig, &sub); dbus_message_iter_open_container(iter, 'a', subSig, &sub);
foreach(k, v; arg) { foreach (k, v; arg) {
DBusMessageIter entry; DBusMessageIter entry;
dbus_message_iter_open_container(&sub, 'e', null, &entry); dbus_message_iter_open_container(&sub, 'e', null, &entry);
buildIter(&entry, k); buildIter(&entry, k);
@ -59,12 +60,11 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
dbus_message_iter_close_container(&sub, &entry); dbus_message_iter_close_container(&sub, &entry);
} }
dbus_message_iter_close_container(iter, &sub); dbus_message_iter_close_container(iter, &sub);
} else static if(isInstanceOf!(VariantN, T)) { } else static if (isInstanceOf!(VariantN, T)) {
enforce(arg.hasValue, enforce(arg.hasValue, new InvalidValueException(arg, "dbus:" ~ cast(char) typeCode!T));
new InvalidValueException(arg, "dbus:" ~ cast(char) typeCode!T));
DBusMessageIter sub; DBusMessageIter sub;
foreach(AT; T.AllowedTypes) { foreach (AT; T.AllowedTypes) {
if (arg.peek!AT) { if (arg.peek!AT) {
dbus_message_iter_open_container(iter, 'v', typeSig!AT.ptr, &sub); dbus_message_iter_open_container(iter, 'v', typeSig!AT.ptr, &sub);
buildIter(&sub, arg.get!AT); buildIter(&sub, arg.get!AT);
@ -72,8 +72,8 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
break; break;
} }
} }
} else static if(is(T == DBusAny) || is(T == Variant!DBusAny)) { } else static if (is(T == DBusAny) || is(T == Variant!DBusAny)) {
static if(is(T == Variant!DBusAny)) { static if (is(T == Variant!DBusAny)) {
auto val = arg.data; auto val = arg.data;
val.explicitVariant = true; val.explicitVariant = true;
} else { } else {
@ -81,66 +81,81 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
} }
DBusMessageIter subStore; DBusMessageIter subStore;
DBusMessageIter* sub = &subStore; DBusMessageIter* sub = &subStore;
const(char)[] sig = [ cast(char) val.type ]; const(char)[] sig = [cast(char) val.type];
if(val.type == 'a') if (val.type == 'a') {
sig ~= val.signature; sig ~= val.signature;
else if(val.type == 'r') } else if (val.type == 'r') {
sig = val.signature; sig = val.signature;
}
sig ~= '\0'; sig ~= '\0';
if (!val.explicitVariant)
if (!val.explicitVariant) {
sub = iter; sub = iter;
else } else {
dbus_message_iter_open_container(iter, 'v', sig.ptr, sub); dbus_message_iter_open_container(iter, 'v', sig.ptr, sub);
if(val.type == 's') { }
if (val.type == 's') {
buildIter(sub, val.str); buildIter(sub, val.str);
} else if(val.type == 'o') { } else if (val.type == 'o') {
buildIter(sub, val.obj); buildIter(sub, val.obj);
} else if(val.type == 'b') { } else if (val.type == 'b') {
buildIter(sub,val.boolean); buildIter(sub, val.boolean);
} else if(dbus_type_is_basic(val.type)) { } else if (dbus_type_is_basic(val.type)) {
dbus_message_iter_append_basic(sub,val.type,&val.int64); dbus_message_iter_append_basic(sub, val.type, &val.int64);
} else if(val.type == 'a') { } else if (val.type == 'a') {
DBusMessageIter arr; DBusMessageIter arr;
dbus_message_iter_open_container(sub, 'a', sig[1 .. $].ptr, &arr); dbus_message_iter_open_container(sub, 'a', sig[1 .. $].ptr, &arr);
if (val.signature == ['y'])
foreach (item; val.binaryData) if (val.signature == ['y']) {
foreach (item; val.binaryData) {
dbus_message_iter_append_basic(&arr, 'y', &item); dbus_message_iter_append_basic(&arr, 'y', &item);
else }
foreach(item; val.array) } else {
foreach (item; val.array) {
buildIter(&arr, item); buildIter(&arr, item);
}
}
dbus_message_iter_close_container(sub, &arr); dbus_message_iter_close_container(sub, &arr);
} else if(val.type == 'r') { } else if (val.type == 'r') {
DBusMessageIter arr; DBusMessageIter arr;
dbus_message_iter_open_container(sub, 'r', null, &arr); dbus_message_iter_open_container(sub, 'r', null, &arr);
foreach(item; val.tuple)
foreach (item; val.tuple) {
buildIter(&arr, item); buildIter(&arr, item);
}
dbus_message_iter_close_container(sub, &arr); dbus_message_iter_close_container(sub, &arr);
} else if(val.type == 'e') { } else if (val.type == 'e') {
DBusMessageIter entry; DBusMessageIter entry;
dbus_message_iter_open_container(sub, 'e', null, &entry); dbus_message_iter_open_container(sub, 'e', null, &entry);
buildIter(&entry, val.entry.key); buildIter(&entry, val.entry.key);
buildIter(&entry, val.entry.value); buildIter(&entry, val.entry.value);
dbus_message_iter_close_container(sub, &entry); dbus_message_iter_close_container(sub, &entry);
} }
if(val.explicitVariant)
if (val.explicitVariant) {
dbus_message_iter_close_container(iter, sub); dbus_message_iter_close_container(iter, sub);
} else static if(isInstanceOf!(Variant, T)) { }
} else static if (isInstanceOf!(Variant, T)) {
DBusMessageIter sub; DBusMessageIter sub;
const(char)* subSig = typeSig!(VariantType!T).toStringz(); const(char)* subSig = typeSig!(VariantType!T).toStringz();
dbus_message_iter_open_container(iter, 'v', subSig, &sub); dbus_message_iter_open_container(iter, 'v', subSig, &sub);
buildIter(&sub, arg.data); buildIter(&sub, arg.data);
dbus_message_iter_close_container(iter, &sub); dbus_message_iter_close_container(iter, &sub);
} else static if(is(T == struct)) { } else static if (is(T == struct)) {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_open_container(iter, 'r', null, &sub); dbus_message_iter_open_container(iter, 'r', null, &sub);
// Following failed because of missing 'this' for members of arg. // Following failed because of missing 'this' for members of arg.
// That sucks. It worked without Filter. // That sucks. It worked without Filter.
// Reported: https://issues.dlang.org/show_bug.cgi?id=17692 // Reported: https://issues.dlang.org/show_bug.cgi?id=17692
// buildIter(&sub, Filter!(isAllowedField, arg.tupleof)); // buildIter(&sub, Filter!(isAllowedField, arg.tupleof));
// Using foreach to work around the issue // Using foreach to work around the issue
foreach(i, member; arg.tupleof) { foreach (i, member; arg.tupleof) {
// Ugly, but we need to use tupleof again in the condition, because when // Ugly, but we need to use tupleof again in the condition, because when
// we use `member`, isAllowedField will fail because it'll find this // we use `member`, isAllowedField will fail because it'll find this
// nice `buildIter` function instead of T when it looks up the parent // nice `buildIter` function instead of T when it looks up the parent
@ -150,55 +165,52 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
} }
dbus_message_iter_close_container(iter, &sub); dbus_message_iter_close_container(iter, &sub);
} else static if(basicDBus!T) { } else static if (basicDBus!T) {
dbus_message_iter_append_basic(iter,typeCode!T,&arg); dbus_message_iter_append_basic(iter, typeCode!T, &arg);
} }
} }
} }
T readIter(T)(DBusMessageIter *iter) if (is(T == enum)) { T readIter(T)(DBusMessageIter* iter)
if (is(T == enum)) {
import std.algorithm.searching : canFind; import std.algorithm.searching : canFind;
alias OriginalType!T B; alias OriginalType!T B;
B value = readIter!B(iter); B value = readIter!B(iter);
enforce( enforce(only(EnumMembers!T).canFind(value), new InvalidValueException(value, T.stringof));
only(EnumMembers!T).canFind(value),
new InvalidValueException(value, T.stringof)
);
return cast(T) value; return cast(T) value;
} }
T readIter(T)(DBusMessageIter *iter) if (isInstanceOf!(BitFlags, T)) { T readIter(T)(DBusMessageIter* iter)
if (isInstanceOf!(BitFlags, T)) {
import std.algorithm.iteration : fold; import std.algorithm.iteration : fold;
alias TemplateArgsOf!T[0] E; alias TemplateArgsOf!T[0] E;
alias OriginalType!E B; alias OriginalType!E B;
B mask = only(EnumMembers!E).fold!((a, b) => cast(B) (a | b)); B mask = only(EnumMembers!E).fold!((a, b) => cast(B)(a | b));
B value = readIter!B(iter); B value = readIter!B(iter);
enforce( enforce(!(value & ~mask), new InvalidValueException(value, T.stringof));
!(value & ~mask),
new InvalidValueException(value, T.stringof)
);
return T(cast(E) value); return T(cast(E) value);
} }
T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFlags, T) && canDBus!T) { T readIter(T)(DBusMessageIter* iter)
if (!is(T == enum) && !isInstanceOf!(BitFlags, T) && canDBus!T) {
auto argType = dbus_message_iter_get_arg_type(iter); auto argType = dbus_message_iter_get_arg_type(iter);
T ret; T ret;
static if(!isInstanceOf!(Variant, T) || is(T == Variant!DBusAny)) { static if (!isInstanceOf!(Variant, T) || is(T == Variant!DBusAny)) {
if(argType == 'v') { if (argType == 'v') {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
static if(is(T == Variant!DBusAny)) { static if (is(T == Variant!DBusAny)) {
ret = variant(readIter!DBusAny(&sub)); ret = variant(readIter!DBusAny(&sub));
} else { } else {
ret = readIter!T(&sub); ret = readIter!T(&sub);
static if(is(T == DBusAny)) static if (is(T == DBusAny))
ret.explicitVariant = true; ret.explicitVariant = true;
} }
dbus_message_iter_next(iter); dbus_message_iter_next(iter);
@ -206,36 +218,35 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
} }
} }
static if( static if (!is(T == DBusAny) && !is(T == Variant!DBusAny) && !isInstanceOf!(VariantN, T)) {
!is(T == DBusAny) enforce(argType == typeCode!T(), new TypeMismatchException(typeCode!T(), argType));
&& !is(T == Variant!DBusAny)
&& !isInstanceOf!(VariantN, T)
) {
enforce(argType == typeCode!T(),
new TypeMismatchException(typeCode!T(), argType));
} }
static if(is(T==string) || is(T==ObjectPath)) {
static if (is(T == string) || is(T == ObjectPath)) {
const(char)* cStr; const(char)* cStr;
dbus_message_iter_get_basic(iter, &cStr); dbus_message_iter_get_basic(iter, &cStr);
string str = cStr.fromStringz().idup; // copy string string str = cStr.fromStringz().idup; // copy string
static if(is(T==string)) static if (is(T == string)) {
ret = str; ret = str;
else } else {
ret = ObjectPath(str); ret = ObjectPath(str);
} else static if(is(T==bool)) { }
} else static if (is(T == bool)) {
dbus_bool_t longerBool; dbus_bool_t longerBool;
dbus_message_iter_get_basic(iter, &longerBool); dbus_message_iter_get_basic(iter, &longerBool);
ret = cast(bool)longerBool; ret = cast(bool) longerBool;
} else static if(isTuple!T) { } else static if (isTuple!T) {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
readIterTuple!T(&sub, ret); readIterTuple!T(&sub, ret);
} else static if(is(T t : U[], U)) { } else static if (is(T t : U[], U)) {
assert(dbus_message_iter_get_element_type(iter) == typeCode!U); assert(dbus_message_iter_get_element_type(iter) == typeCode!U);
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
while(dbus_message_iter_get_arg_type(&sub) != 0) {
static if(is(U == DictionaryEntry!(K,V), K, V)) { while (dbus_message_iter_get_arg_type(&sub) != 0) {
static if (is(U == DictionaryEntry!(K, V), K, V)) {
DBusMessageIter entry; DBusMessageIter entry;
dbus_message_iter_recurse(&sub, &entry); dbus_message_iter_recurse(&sub, &entry);
ret ~= U(readIter!K(&entry), readIter!V(&entry)); ret ~= U(readIter!K(&entry), readIter!V(&entry));
@ -244,17 +255,16 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
ret ~= readIter!U(&sub); ret ~= readIter!U(&sub);
} }
} }
} else static if(isInstanceOf!(Variant, T)) { } else static if (isInstanceOf!(Variant, T)) {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
ret.data = readIter!(VariantType!T)(&sub); ret.data = readIter!(VariantType!T)(&sub);
} else static if(isInstanceOf!(VariantN, T)) { } else static if (isInstanceOf!(VariantN, T)) {
scope const(char)[] argSig = scope const(char)[] argSig = dbus_message_iter_get_signature(iter).fromStringz();
dbus_message_iter_get_signature(iter).fromStringz(); scope (exit)
scope(exit)
dbus_free(cast(void*) argSig.ptr); dbus_free(cast(void*) argSig.ptr);
foreach(AT; T.AllowedTypes) { foreach (AT; T.AllowedTypes) {
// We have to compare the full signature here, not just the typecode. // We have to compare the full signature here, not just the typecode.
// Otherwise, in case of container types, we might select the wrong one. // Otherwise, in case of container types, we might select the wrong one.
// We would then be calling an incorrect instance of readIter, which would // We would then be calling an incorrect instance of readIter, which would
@ -267,10 +277,11 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
// If no value is in ret, apparently none of the types matched. // If no value is in ret, apparently none of the types matched.
enforce(ret.hasValue, new TypeMismatchException(typeCode!T, argType)); enforce(ret.hasValue, new TypeMismatchException(typeCode!T, argType));
} else static if(isAssociativeArray!T) { } else static if (isAssociativeArray!T) {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
while(dbus_message_iter_get_arg_type(&sub) != 0) {
while (dbus_message_iter_get_arg_type(&sub) != 0) {
DBusMessageIter entry; DBusMessageIter entry;
dbus_message_iter_recurse(&sub, &entry); dbus_message_iter_recurse(&sub, &entry);
auto k = readIter!(KeyType!T)(&entry); auto k = readIter!(KeyType!T)(&entry);
@ -278,59 +289,64 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
ret[k] = v; ret[k] = v;
dbus_message_iter_next(&sub); dbus_message_iter_next(&sub);
} }
} else static if(is(T == DBusAny)) { } else static if (is(T == DBusAny)) {
ret.type = argType; ret.type = argType;
ret.explicitVariant = false; ret.explicitVariant = false;
if(ret.type == 's') {
if (ret.type == 's') {
ret.str = readIter!string(iter); ret.str = readIter!string(iter);
return ret; return ret;
} else if(ret.type == 'o') { } else if (ret.type == 'o') {
ret.obj = readIter!ObjectPath(iter); ret.obj = readIter!ObjectPath(iter);
return ret; return ret;
} else if(ret.type == 'b') { } else if (ret.type == 'b') {
ret.boolean = readIter!bool(iter); ret.boolean = readIter!bool(iter);
return ret; return ret;
} else if(dbus_type_is_basic(ret.type)) { } else if (dbus_type_is_basic(ret.type)) {
dbus_message_iter_get_basic(iter, &ret.int64); dbus_message_iter_get_basic(iter, &ret.int64);
} else if(ret.type == 'a') { } else if (ret.type == 'a') {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
auto sig = dbus_message_iter_get_signature(&sub); auto sig = dbus_message_iter_get_signature(&sub);
ret.signature = sig.fromStringz.dup; ret.signature = sig.fromStringz.dup;
dbus_free(sig); dbus_free(sig);
if (ret.signature == ['y']) if (ret.signature == ['y']) {
while(dbus_message_iter_get_arg_type(&sub) != 0) { while (dbus_message_iter_get_arg_type(&sub) != 0) {
ubyte b; ubyte b;
assert(dbus_message_iter_get_arg_type(&sub) == 'y'); assert(dbus_message_iter_get_arg_type(&sub) == 'y');
dbus_message_iter_get_basic(&sub, &b); dbus_message_iter_get_basic(&sub, &b);
dbus_message_iter_next(&sub); dbus_message_iter_next(&sub);
ret.binaryData ~= b; ret.binaryData ~= b;
} }
else } else {
while(dbus_message_iter_get_arg_type(&sub) != 0) { while (dbus_message_iter_get_arg_type(&sub) != 0) {
ret.array ~= readIter!DBusAny(&sub); ret.array ~= readIter!DBusAny(&sub);
} }
} else if(ret.type == 'r') { }
} else if (ret.type == 'r') {
auto sig = dbus_message_iter_get_signature(iter); auto sig = dbus_message_iter_get_signature(iter);
ret.signature = sig.fromStringz.dup; ret.signature = sig.fromStringz.dup;
dbus_free(sig); dbus_free(sig);
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
while(dbus_message_iter_get_arg_type(&sub) != 0) {
while (dbus_message_iter_get_arg_type(&sub) != 0) {
ret.tuple ~= readIter!DBusAny(&sub); ret.tuple ~= readIter!DBusAny(&sub);
} }
} else if(ret.type == 'e') { } else if (ret.type == 'e') {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
ret.entry = new DictionaryEntry!(DBusAny, DBusAny); ret.entry = new DictionaryEntry!(DBusAny, DBusAny);
ret.entry.key = readIter!DBusAny(&sub); ret.entry.key = readIter!DBusAny(&sub);
ret.entry.value = readIter!DBusAny(&sub); ret.entry.value = readIter!DBusAny(&sub);
} }
} else static if(is(T == struct)) { } else static if (is(T == struct)) {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
readIterStruct!T(&sub, ret); readIterStruct!T(&sub, ret);
} else static if(basicDBus!T) { } else static if (basicDBus!T) {
dbus_message_iter_get_basic(iter, &ret); dbus_message_iter_get_basic(iter, &ret);
} }
@ -338,15 +354,16 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
return ret; return ret;
} }
void readIterTuple(Tup)(DBusMessageIter *iter, ref Tup tuple) if(isTuple!Tup && allCanDBus!(Tup.Types)) { void readIterTuple(Tup)(DBusMessageIter* iter, ref Tup tuple)
foreach(index, T; Tup.Types) { if (isTuple!Tup && allCanDBus!(Tup.Types)) {
foreach (index, T; Tup.Types) {
tuple[index] = readIter!T(iter); tuple[index] = readIter!T(iter);
} }
} }
void readIterStruct(S)(DBusMessageIter *iter, ref S s) if(is(S == struct) && canDBus!S) void readIterStruct(S)(DBusMessageIter* iter, ref S s)
{ if (is(S == struct) && canDBus!S) {
foreach(index, T; Fields!S) { foreach (index, T; Fields!S) {
static if (isAllowedField!(s.tupleof[index])) { static if (isAllowedField!(s.tupleof[index])) {
s.tupleof[index] = readIter!T(iter); s.tupleof[index] = readIter!T(iter);
} }
@ -356,8 +373,12 @@ void readIterStruct(S)(DBusMessageIter *iter, ref S s) if(is(S == struct) && can
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
import ddbus.thin; import ddbus.thin;
Variant!T var(T)(T data){ return Variant!T(data); }
Message msg = Message("org.example.wow","/wut","org.test.iface","meth"); Variant!T var(T)(T data) {
return Variant!T(data);
}
Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth");
bool[] emptyB; bool[] emptyB;
string[string] map; string[string] map;
map["hello"] = "world"; map["hello"] = "world";
@ -365,24 +386,24 @@ unittest {
anyVar.type.assertEqual('t'); anyVar.type.assertEqual('t');
anyVar.uint64.assertEqual(1561); anyVar.uint64.assertEqual(1561);
anyVar.explicitVariant.assertEqual(false); anyVar.explicitVariant.assertEqual(false);
auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32, [1, 2], tuple(variant(4), 5), map)); auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32,
Variant!DBusAny complexVar = variant(DBusAny([ [1, 2], tuple(variant(4), 5), map));
"hello world": variant(DBusAny(1337)), Variant!DBusAny complexVar = variant(DBusAny(["hello world" : variant(DBusAny(1337)),
"array value": variant(DBusAny([42, 64])), "array value" : variant(DBusAny([42, 64])), "tuple value"
"tuple value": variant(tupleMember), : variant(tupleMember), "optimized binary data"
"optimized binary data": variant(DBusAny(cast(ubyte[]) [1, 2, 3, 4, 5, 6])) : variant(DBusAny(cast(ubyte[])[1, 2, 3, 4, 5, 6]))]));
]));
complexVar.data.type.assertEqual('a'); complexVar.data.type.assertEqual('a');
complexVar.data.signature.assertEqual("{sv}".dup); complexVar.data.signature.assertEqual("{sv}".dup);
tupleMember.signature.assertEqual("(vviai(vi)a{ss})"); tupleMember.signature.assertEqual("(vviai(vi)a{ss})");
auto args = tuple(5,true,"wow",var(5.9),[6,5],tuple(6.2,4,[["lol"]],emptyB,var([4,2])),map,anyVar,complexVar); 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.build(args.expand);
msg.signature().assertEqual("ibsvai(diaasabv)a{ss}tv"); msg.signature().assertEqual("ibsvai(diaasabv)a{ss}tv");
msg.read!string().assertThrow!TypeMismatchException(); msg.read!string().assertThrow!TypeMismatchException();
msg.readTuple!(Tuple!(int, bool, double)).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!(Tuple!(int, bool, string, double)).assertEqual(tuple(5, true, "wow", 5.9));
msg.readTuple!(typeof(args))().assertEqual(args); msg.readTuple!(typeof(args))().assertEqual(args);
DBusMessageIter iter; DBusMessageIter iter;
@ -391,13 +412,14 @@ unittest {
readIter!bool(&iter).assertEqual(true); readIter!bool(&iter).assertEqual(true);
readIter!string(&iter).assertEqual("wow"); readIter!string(&iter).assertEqual("wow");
readIter!double(&iter).assertEqual(5.9); readIter!double(&iter).assertEqual(5.9);
readIter!(int[])(&iter).assertEqual([6,5]); readIter!(int[])(&iter).assertEqual([6, 5]);
readIter!(Tuple!(double,int,string[][],bool[],Variant!(int[])))(&iter).assertEqual(tuple(6.2,4,[["lol"]],emptyB,var([4,2]))); readIter!(Tuple!(double, int, string[][], bool[], Variant!(int[])))(&iter).assertEqual(
tuple(6.2, 4, [["lol"]], emptyB, var([4, 2])));
// There are two ways to read a dictionary, so duplicate the iterator to test both. // There are two ways to read a dictionary, so duplicate the iterator to test both.
auto iter2 = iter; auto iter2 = iter;
readIter!(string[string])(&iter).assertEqual(["hello": "world"]); readIter!(string[string])(&iter).assertEqual(["hello" : "world"]);
auto dict = readIter!(DictionaryEntry!(string,string)[])(&iter2); auto dict = readIter!(DictionaryEntry!(string, string)[])(&iter2);
dict.length.assertEqual(1); dict.length.assertEqual(1);
dict[0].key.assertEqual("hello"); dict[0].key.assertEqual("hello");
dict[0].value.assertEqual("world"); dict[0].value.assertEqual("world");
@ -412,8 +434,18 @@ unittest {
import std.variant : Algebraic; import std.variant : Algebraic;
enum E : int { a, b, c } enum E : int {
enum F : uint { x = 1, y = 2, z = 4 } a,
b,
c
}
enum F : uint {
x = 1,
y = 2,
z = 4
}
alias V = Algebraic!(byte, short, int, long, string); alias V = Algebraic!(byte, short, int, long, string);
Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth2"); Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth2");
@ -437,4 +469,3 @@ unittest {
readIter!V(&iter).assertEqual(v1); readIter!V(&iter).assertEqual(v1);
readIter!short(&iter).assertEqual(v2.get!short); readIter!short(&iter).assertEqual(v2.get!short);
} }

View file

@ -2,16 +2,12 @@ module ddbus.exception;
import ddbus.c_lib; import ddbus.c_lib;
package T wrapErrors(T)( package T wrapErrors(T)(T delegate(DBusError* err) del, string file = __FILE__,
T delegate(DBusError *err) del, size_t line = __LINE__, Throwable next = null) {
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null
) {
DBusError error; DBusError error;
dbus_error_init(&error); dbus_error_init(&error);
T ret = del(&error); T ret = del(&error);
if(dbus_error_is_set(&error)) { if (dbus_error_is_set(&error)) {
auto ex = new DBusException(&error, file, line, next); auto ex = new DBusException(&error, file, line, next);
dbus_error_free(&error); dbus_error_free(&error);
throw ex; throw ex;
@ -23,12 +19,8 @@ package T wrapErrors(T)(
Thrown when a DBus error code was returned by libdbus. Thrown when a DBus error code was returned by libdbus.
+/ +/
class DBusException : Exception { class DBusException : Exception {
private this( private this(scope DBusError* err, string file = __FILE__,
scope DBusError *err, size_t line = __LINE__, Throwable next = null) pure nothrow {
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null
) pure nothrow {
import std.string : fromStringz; import std.string : fromStringz;
super(err.message.fromStringz().idup, file, line, next); super(err.message.fromStringz().idup, file, line, next);
@ -41,37 +33,27 @@ class DBusException : Exception {
of its actual value. of its actual value.
+/ +/
class TypeMismatchException : Exception { class TypeMismatchException : Exception {
package this( package this(int expectedType, int actualType, string file = __FILE__,
int expectedType, size_t line = __LINE__, Throwable next = null) pure nothrow @safe {
int actualType,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null
) pure nothrow @safe {
string message; string message;
// dfmt off
if (expectedType == 'v') { if (expectedType == 'v') {
message = "The type of value at the current position in the message is" message = "The type of value at the current position in the message is"
~ " incompatible to the target variant type." ~ " incompatible to the target variant type." ~ " Type code of the value: '"
~ " Type code of the value: '" ~ cast(char) actualType ~ '\''; ~ cast(char) actualType ~ '\'';
} else { } else {
message = "The type of value at the current position in the message does" message = "The type of value at the current position in the message does"
~ " not match the type of value to be read." ~ " not match the type of value to be read." ~ " Expected: '"
~ " Expected: '" ~ cast(char) expectedType ~ "'," ~ cast(char) expectedType ~ "'," ~ " Got: '" ~ cast(char) actualType ~ '\'';
~ " Got: '" ~ cast(char) actualType ~ '\'';
} }
// dfmt on
this(message, expectedType, actualType, file, line, next); this(message, expectedType, actualType, file, line, next);
} }
this( this(string message, int expectedType, int actualType, string file = __FILE__,
string message, size_t line = __LINE__, Throwable next = null) pure nothrow @safe {
int expectedType,
int actualType,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null
) pure nothrow @safe {
_expectedType = expectedType; _expectedType = expectedType;
_actualType = actualType; _actualType = actualType;
super(message, file, line, next); super(message, file, line, next);
@ -85,7 +67,7 @@ class TypeMismatchException : Exception {
return _actualType; return _actualType;
} }
private: private:
int _expectedType; int _expectedType;
int _actualType; int _actualType;
} }
@ -98,19 +80,15 @@ class TypeMismatchException : Exception {
that have a constrained value set, such as Enums. that have a constrained value set, such as Enums.
+/ +/
class InvalidValueException : Exception { class InvalidValueException : Exception {
package this(Source)( package this(Source)(Source value, string targetType, string file = __FILE__,
Source value, size_t line = __LINE__, Throwable next = null) {
string targetType,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null
) {
import std.conv : to; import std.conv : to;
static if(__traits(compiles, value.to!string)) static if (__traits(compiles, value.to!string)) {
string valueString = value.to!string; string valueString = value.to!string;
else } else {
string valueString = "(unprintable)"; string valueString = "(unprintable)";
}
super("Value " ~ valueString ~ " cannot be represented in type " ~ targetType); super("Value " ~ valueString ~ " cannot be represented in type " ~ targetType);
} }

View file

@ -1,3 +1,5 @@
module ddbus;
public import ddbus.thin; public import ddbus.thin;
public import ddbus.router; public import ddbus.router;
public import ddbus.bus; public import ddbus.bus;

View file

@ -36,19 +36,20 @@ struct MessagePattern {
hash += stringHash(&path); hash += stringHash(&path);
hash += stringHash(&iface); hash += stringHash(&iface);
hash += stringHash(&method); hash += stringHash(&method);
hash += (signal?1:0); hash += (signal ? 1 : 0);
return hash; return hash;
} }
bool opEquals(ref const this s) const @safe pure nothrow { bool opEquals(ref const typeof(this) s) const @safe pure nothrow {
return (path == s.path) && (iface == s.iface) && (method == s.method) && (signal == s.signal); return (path == s.path) && (iface == s.iface) && (method == s.method) && (signal == s.signal);
} }
} }
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
auto msg = Message("org.example.test", "/test","org.example.testing","testMethod");
auto patt= new MessagePattern(msg); auto msg = Message("org.example.test", "/test", "org.example.testing", "testMethod");
auto patt = new MessagePattern(msg);
patt.assertEqual(patt); patt.assertEqual(patt);
patt.signal.assertFalse(); patt.signal.assertFalse();
patt.path.assertEqual("/test"); patt.path.assertEqual("/test");
@ -66,30 +67,34 @@ class MessageRouter {
bool handle(Message msg, Connection conn) { bool handle(Message msg, Connection conn) {
MessageType type = msg.type(); MessageType type = msg.type();
if(type != MessageType.Call && type != MessageType.Signal) if (type != MessageType.Call && type != MessageType.Signal) {
return false; return false;
}
auto pattern = MessagePattern(msg); auto pattern = MessagePattern(msg);
// import std.stdio; debug writeln("Handling ", pattern); // import std.stdio; debug writeln("Handling ", pattern);
if(pattern.iface == "org.freedesktop.DBus.Introspectable" && if (pattern.iface == "org.freedesktop.DBus.Introspectable"
pattern.method == "Introspect" && !pattern.signal) { && pattern.method == "Introspect" && !pattern.signal) {
handleIntrospect(pattern.path, msg, conn); handleIntrospect(pattern.path, msg, conn);
return true; return true;
} }
MessageHandler* handler = (pattern in callTable); MessageHandler* handler = (pattern in callTable);
if(handler is null) return false; if (handler is null) {
return false;
}
// Check for matching argument types // Check for matching argument types
version(DDBusNoChecking) { version (DDBusNoChecking) {
} else { } else {
if(!equal(join(handler.argSig), msg.signature())) { if (!equal(join(handler.argSig), msg.signature())) {
return false; return false;
} }
} }
handler.func(msg,conn); handler.func(msg, conn);
return true; return true;
} }
@ -97,25 +102,39 @@ class MessageRouter {
void handlerWrapper(Message call, Connection conn) { void handlerWrapper(Message call, Connection conn) {
Tuple!Args args = call.readTuple!(Tuple!Args)(); Tuple!Args args = call.readTuple!(Tuple!Args)();
auto retMsg = call.createReturn(); auto retMsg = call.createReturn();
static if(!is(Ret == void)) {
static if (!is(Ret == void)) {
Ret ret = handler(args.expand); Ret ret = handler(args.expand);
static if (is(Ret == Tuple!T, T...)) static if (is(Ret == Tuple!T, T...)) {
retMsg.build!T(ret.expand); retMsg.build!T(ret.expand);
else } else {
retMsg.build(ret); retMsg.build(ret);
}
} else { } else {
handler(args.expand); handler(args.expand);
} }
if(!patt.signal)
if (!patt.signal) {
conn.send(retMsg); conn.send(retMsg);
}
} }
static string[] args = typeSigArr!Args; static string[] args = typeSigArr!Args;
static if(is(Ret==void)) {
static if (is(Ret == void)) {
static string[] ret = []; static string[] ret = [];
} else { } else {
static string[] ret = typeSigReturn!Ret; static string[] ret = typeSigReturn!Ret;
} }
MessageHandler handleStruct = {func: &handlerWrapper, argSig: args, retSig: ret};
// dfmt off
MessageHandler handleStruct = {
func: &handlerWrapper,
argSig: args,
retSig: ret
};
// dfmt on
callTable[patt] = handleStruct; callTable[patt] = handleStruct;
} }
@ -123,38 +142,50 @@ class MessageRouter {
<node name="%s">`; <node name="%s">`;
string introspectXML(string path) { string introspectXML(string path) {
auto methods = callTable.byKey().filter!(a => (a.path == path) && !a.signal)().array() // dfmt off
// .schwartzSort!((a) => a.iface, "a<b")(); auto methods = callTable
.sort!((a,b) => a.iface < b.iface)(); .byKey()
.filter!(a => (a.path == path) && !a.signal)
.array
.sort!((a, b) => a.iface < b.iface)();
// dfmt on
auto ifaces = methods.groupBy(); auto ifaces = methods.groupBy();
auto app = appender!string; auto app = appender!string;
formattedWrite(app,introspectHeader,path); formattedWrite(app, introspectHeader, path);
foreach(iface; ifaces) { foreach (iface; ifaces) {
formattedWrite(app,`<interface name="%s">`,iface.front.iface); formattedWrite(app, `<interface name="%s">`, iface.front.iface);
foreach(methodPatt; iface.array()) {
formattedWrite(app,`<method name="%s">`,methodPatt.method); foreach (methodPatt; iface.array()) {
formattedWrite(app, `<method name="%s">`, methodPatt.method);
auto handler = callTable[methodPatt]; auto handler = callTable[methodPatt];
foreach(arg; handler.argSig) {
formattedWrite(app,`<arg type="%s" direction="in"/>`,arg); foreach (arg; handler.argSig) {
formattedWrite(app, `<arg type="%s" direction="in"/>`, arg);
} }
foreach(arg; handler.retSig) {
formattedWrite(app,`<arg type="%s" direction="out"/>`,arg); foreach (arg; handler.retSig) {
formattedWrite(app, `<arg type="%s" direction="out"/>`, arg);
} }
app.put("</method>"); app.put("</method>");
} }
app.put("</interface>"); app.put("</interface>");
} }
string childPath = path; string childPath = path;
if(!childPath.endsWith("/")) { if (!childPath.endsWith("/")) {
childPath ~= "/"; childPath ~= "/";
} }
auto children = callTable.byKey().filter!(a => (a.path.startsWith(childPath)) && !a.signal)()
.map!((s) => s.path.chompPrefix(childPath)) auto children = callTable.byKey()
.map!((s) => s.splitter('/').front) .filter!(a => (a.path.startsWith(childPath)) && !a.signal)().map!(
.array().sort().uniq(); (s) => s.path.chompPrefix(childPath)).map!((s) => s.splitter('/')
foreach(child; children) { .front).array().sort().uniq();
formattedWrite(app,`<node name="%s"/>`,child);
foreach (child; children) {
formattedWrite(app, `<node name="%s"/>`, child);
} }
app.put("</node>"); app.put("</node>");
@ -168,31 +199,33 @@ class MessageRouter {
} }
} }
extern(C) private DBusHandlerResult filterFunc(DBusConnection *dConn, DBusMessage *dMsg, void *routerP) { extern (C) private DBusHandlerResult filterFunc(DBusConnection* dConn,
MessageRouter router = cast(MessageRouter)routerP; DBusMessage* dMsg, void* routerP) {
MessageRouter router = cast(MessageRouter) routerP;
dbus_message_ref(dMsg); dbus_message_ref(dMsg);
Message msg = Message(dMsg); Message msg = Message(dMsg);
dbus_connection_ref(dConn); dbus_connection_ref(dConn);
Connection conn = Connection(dConn); Connection conn = Connection(dConn);
bool handled = router.handle(msg, conn); bool handled = router.handle(msg, conn);
if(handled) {
if (handled) {
return DBusHandlerResult.DBUS_HANDLER_RESULT_HANDLED; return DBusHandlerResult.DBUS_HANDLER_RESULT_HANDLED;
} else { } else {
return DBusHandlerResult.DBUS_HANDLER_RESULT_NOT_YET_HANDLED; return DBusHandlerResult.DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
} }
} }
extern(C) private void unrootUserData(void *userdata) { extern (C) private void unrootUserData(void* userdata) {
GC.removeRoot(userdata); GC.removeRoot(userdata);
} }
void registerRouter(Connection conn, MessageRouter router) { void registerRouter(Connection conn, MessageRouter router) {
void *routerP = cast(void*)router; void* routerP = cast(void*) router;
GC.addRoot(routerP); GC.addRoot(routerP);
dbus_connection_add_filter(conn.conn, &filterFunc, routerP, &unrootUserData); dbus_connection_add_filter(conn.conn, &filterFunc, routerP, &unrootUserData);
} }
unittest{ unittest {
import dunit.toolkit; import dunit.toolkit;
import std.typecons : BitFlags; import std.typecons : BitFlags;
@ -200,31 +233,53 @@ unittest{
auto router = new MessageRouter(); auto router = new MessageRouter();
// set up test messages // set up test messages
MessagePattern patt = MessagePattern("/root","ca.thume.test","test"); MessagePattern patt = MessagePattern("/root", "ca.thume.test", "test");
router.setHandler!(int,int)(patt,(int p) {return 6;}); router.setHandler!(int, int)(patt, (int p) { return 6; });
patt = MessagePattern("/root","ca.thume.tester","lolwut"); patt = MessagePattern("/root", "ca.thume.tester", "lolwut");
router.setHandler!(void,int,string)(patt,(int p, string p2) {}); router.setHandler!(void, int, string)(patt, (int p, string p2) { });
patt = MessagePattern("/root/wat","ca.thume.tester","lolwut"); patt = MessagePattern("/root/wat", "ca.thume.tester", "lolwut");
router.setHandler!(int,int)(patt,(int p) {return 6;}); router.setHandler!(int, int)(patt, (int p) { return 6; });
patt = MessagePattern("/root/bar","ca.thume.tester","lolwut"); patt = MessagePattern("/root/bar", "ca.thume.tester", "lolwut");
router.setHandler!(Variant!DBusAny,int)(patt,(int p) {return variant(DBusAny(p));}); router.setHandler!(Variant!DBusAny, int)(patt, (int p) {
patt = MessagePattern("/root/foo","ca.thume.tester","lolwut"); return variant(DBusAny(p));
router.setHandler!(Tuple!(string,string,int),int,Variant!DBusAny)(patt,(int p, Variant!DBusAny any) {Tuple!(string,string,int) ret; ret[0] = "a"; ret[1] = "b"; ret[2] = p; return ret;}); });
patt = MessagePattern("/troll","ca.thume.tester","wow"); patt = MessagePattern("/root/foo", "ca.thume.tester", "lolwut");
router.setHandler!(void)(patt,{return;}); router.setHandler!(Tuple!(string, string, int), int,
Variant!DBusAny)(patt, (int p, Variant!DBusAny any) {
Tuple!(string, string, int) ret;
ret[0] = "a";
ret[1] = "b";
ret[2] = p;
return ret;
});
patt = MessagePattern("/troll", "ca.thume.tester", "wow");
router.setHandler!(void)(patt, { return; });
patt = MessagePattern("/root/fancy", "ca.thume.tester", "crazyTest");
enum F : ushort {
a = 1,
b = 8,
c = 16
}
struct S {
byte b;
ulong ul;
F f;
}
patt = MessagePattern("/root/fancy","ca.thume.tester","crazyTest");
enum F : ushort { a = 1, b = 8, c = 16 }
struct S { byte b; ulong ul; F f; }
router.setHandler!(int)(patt, (Algebraic!(ushort, BitFlags!F, S) v) { router.setHandler!(int)(patt, (Algebraic!(ushort, BitFlags!F, S) v) {
if (v.type is typeid(ushort) || v.type is typeid(BitFlags!F)) { if (v.type is typeid(ushort) || v.type is typeid(BitFlags!F)) {
return v.coerce!int; return v.coerce!int;
} else if (v.type is typeid(S)) { } else if (v.type is typeid(S)) {
auto s = v.get!S; auto s = v.get!S;
final switch (s.f) { final switch (s.f) {
case F.a: return s.b; case F.a:
case F.b: return cast(int) s.ul; return s.b;
case F.c: return cast(int) s.ul + s.b; case F.b:
return cast(int) s.ul;
case F.c:
return cast(int) s.ul + s.b;
} }
} }
@ -232,9 +287,9 @@ unittest{
}); });
static assert(!__traits(compiles, { static assert(!__traits(compiles, {
patt = MessagePattern("/root/bar","ca.thume.tester","lolwut"); patt = MessagePattern("/root/bar", "ca.thume.tester", "lolwut");
router.setHandler!(void, DBusAny)(patt,(DBusAny wrongUsage){return;}); router.setHandler!(void, DBusAny)(patt, (DBusAny wrongUsage) { return; });
})); }));
// TODO: these tests rely on nondeterministic hash map ordering // TODO: these tests rely on nondeterministic hash map ordering
static string introspectResult = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> static string introspectResult = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
@ -246,5 +301,6 @@ unittest{
static string introspectResult3 = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> static string introspectResult3 = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/root/fancy"><interface name="ca.thume.tester"><method name="crazyTest"><arg type="v" direction="in"/><arg type="i" direction="out"/></method></interface></node>`; <node name="/root/fancy"><interface name="ca.thume.tester"><method name="crazyTest"><arg type="v" direction="in"/><arg type="i" direction="out"/></method></interface></node>`;
router.introspectXML("/root/fancy").assertEqual(introspectResult3); router.introspectXML("/root/fancy").assertEqual(introspectResult3);
router.introspectXML("/").assertEndsWith(`<node name="/"><node name="root"/><node name="troll"/></node>`); router.introspectXML("/")
.assertEndsWith(`<node name="/"><node name="root"/><node name="troll"/></node>`);
} }

View file

@ -19,15 +19,16 @@ class PathIface {
this.iface = iface.toStringz(); this.iface = iface.toStringz();
} }
Ret call(Ret, Args...)(string meth, Args args) if(allCanDBus!Args && canDBus!Ret) { Ret call(Ret, Args...)(string meth, Args args)
Message msg = Message(dbus_message_new_method_call(dest,path,iface,meth.toStringz())); if (allCanDBus!Args && canDBus!Ret) {
Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz()));
msg.build(args); msg.build(args);
Message ret = conn.sendWithReplyBlocking(msg); Message ret = conn.sendWithReplyBlocking(msg);
return ret.read!Ret(); return ret.read!Ret();
} }
Message opDispatch(string meth, Args...)(Args args) { Message opDispatch(string meth, Args...)(Args args) {
Message msg = Message(dbus_message_new_method_call(dest,path,iface,meth.toStringz())); Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz()));
msg.build(args); msg.build(args);
return conn.sendWithReplyBlocking(msg); return conn.sendWithReplyBlocking(msg);
} }
@ -40,12 +41,13 @@ class PathIface {
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
Connection conn = connectToBus(); Connection conn = connectToBus();
PathIface obj = new PathIface(conn, "org.freedesktop.DBus","/org/freedesktop/DBus", PathIface obj = new PathIface(conn, "org.freedesktop.DBus",
"org.freedesktop.DBus"); "/org/freedesktop/DBus", "org.freedesktop.DBus");
auto names = obj.GetNameOwner("org.freedesktop.DBus").to!string(); auto names = obj.GetNameOwner("org.freedesktop.DBus").to!string();
names.assertEqual("org.freedesktop.DBus"); names.assertEqual("org.freedesktop.DBus");
obj.call!string("GetNameOwner","org.freedesktop.DBus").assertEqual("org.freedesktop.DBus"); obj.call!string("GetNameOwner", "org.freedesktop.DBus").assertEqual("org.freedesktop.DBus");
} }
enum SignalMethod; enum SignalMethod;
@ -63,30 +65,39 @@ enum SignalMethod;
could be instantiated with any object efficiently and placed in the router table with minimal duplication. could be instantiated with any object efficiently and placed in the router table with minimal duplication.
*/ */
void registerMethods(T : Object)(MessageRouter router, string path, string iface, T obj) { void registerMethods(T : Object)(MessageRouter router, string path, string iface, T obj) {
MessagePattern patt = MessagePattern(path,iface,"",false); MessagePattern patt = MessagePattern(path, iface, "", false);
foreach(member; __traits(allMembers, T)) { foreach (member; __traits(allMembers, T)) {
// dfmt off
static if (__traits(compiles, __traits(getOverloads, obj, member)) static if (__traits(compiles, __traits(getOverloads, obj, member))
&& __traits(getOverloads, obj, member).length > 0 && __traits(getOverloads, obj, member).length > 0
&& __traits(compiles, router.setHandler(patt, &__traits(getOverloads,obj,member)[0]))) { && __traits(compiles, router.setHandler(patt, &__traits(getOverloads, obj, member)[0]))) {
patt.method = member; patt.method = member;
patt.signal = hasUDA!(__traits(getOverloads,obj,member)[0], SignalMethod); patt.signal = hasUDA!(__traits(getOverloads, obj, member)[0], SignalMethod);
router.setHandler(patt, &__traits(getOverloads,obj,member)[0]); router.setHandler(patt, &__traits(getOverloads, obj, member)[0]);
} }
// dfmt on
} }
} }
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
class Tester { class Tester {
int lol(int x, string s, string[string] map, Variant!DBusAny any) {return 5;} int lol(int x, string s, string[string] map, Variant!DBusAny any) {
void wat() {} return 5;
@SignalMethod }
void signalRecv() {}
void wat() {
}
@SignalMethod void signalRecv() {
}
} }
auto o = new Tester; auto o = new Tester;
auto router = new MessageRouter; auto router = new MessageRouter;
registerMethods(router, "/","ca.thume.test",o); registerMethods(router, "/", "ca.thume.test", o);
MessagePattern patt = MessagePattern("/","ca.thume.test","wat"); MessagePattern patt = MessagePattern("/", "ca.thume.test", "wat");
router.callTable.assertHasKey(patt); router.callTable.assertHasKey(patt);
patt.method = "signalRecv"; patt.method = "signalRecv";
patt.signal = true; patt.signal = true;
@ -95,6 +106,6 @@ unittest {
patt.signal = false; patt.signal = false;
router.callTable.assertHasKey(patt); router.callTable.assertHasKey(patt);
auto res = router.callTable[patt]; auto res = router.callTable[patt];
res.argSig.assertEqual(["i","s","a{ss}","v"]); res.argSig.assertEqual(["i", "s", "a{ss}", "v"]);
res.retSig.assertEqual(["i"]); res.retSig.assertEqual(["i"]);
} }

View file

@ -3,12 +3,13 @@ module ddbus.thin;
import core.time : Duration; import core.time : Duration;
import ddbus.attributes : isAllowedField;
import ddbus.c_lib; import ddbus.c_lib;
import ddbus.conv; import ddbus.conv;
import ddbus.exception : TypeMismatchException; import ddbus.exception : TypeMismatchException;
import ddbus.util; import ddbus.util;
import std.meta : staticIndexOf; import std.meta : ApplyRight, Filter, staticIndexOf;
import std.string; import std.string;
import std.typecons; import std.typecons;
import std.exception; import std.exception;
@ -48,24 +49,28 @@ struct ObjectPath {
} }
ObjectPath opBinary(string op : "~")(string rhs) const pure @safe { ObjectPath opBinary(string op : "~")(string rhs) const pure @safe {
if (!rhs.startsWith("/")) if (!rhs.startsWith("/")) {
return opBinary!"~"(ObjectPath("/" ~ rhs)); return opBinary!"~"(ObjectPath("/" ~ rhs));
else } else {
return opBinary!"~"(ObjectPath(rhs)); return opBinary!"~"(ObjectPath(rhs));
}
} }
ObjectPath opBinary(string op : "~")(ObjectPath rhs) const pure @safe ObjectPath opBinary(string op : "~")(ObjectPath rhs) const pure @safe
in { in {
assert(ObjectPath.isValid(_value) && ObjectPath.isValid(rhs._value)); assert(ObjectPath.isValid(_value) && ObjectPath.isValid(rhs._value));
} out (v) { }
out (v) {
assert(ObjectPath.isValid(v._value)); assert(ObjectPath.isValid(v._value));
} do { }
do {
ObjectPath ret; ObjectPath ret;
if (_value == "/") if (_value == "/") {
ret._value = rhs._value; ret._value = rhs._value;
else } else {
ret._value = _value ~ rhs._value; ret._value = _value ~ rhs._value;
}
return ret; return ret;
} }
@ -85,20 +90,21 @@ struct ObjectPath {
static bool isValid(string objPath) pure @nogc nothrow @safe { static bool isValid(string objPath) pure @nogc nothrow @safe {
import std.ascii : isAlphaNum; import std.ascii : isAlphaNum;
if (!objPath.length) if (!objPath.length) {
return false; return false;
if (objPath == "/") }
if (objPath == "/") {
return true; return true;
if (objPath[0] != '/' || objPath[$ - 1] == '/') }
if (objPath[0] != '/' || objPath[$ - 1] == '/') {
return false; return false;
}
// .representation to avoid unicode exceptions -> @nogc & nothrow // .representation to avoid unicode exceptions -> @nogc & nothrow
return objPath.representation.splitter('/').drop(1) return objPath.representation.splitter('/').drop(1).all!(a => a.length
.all!(a => && a.all!(c => c.isAlphaNum || c == '_'));
a.length &&
a.all!(c =>
c.isAlphaNum || c == '_'
)
);
} }
} }
@ -137,8 +143,7 @@ struct DBusAny {
/// Same functionality as Variant!T but with dynamic types if true. /// Same functionality as Variant!T but with dynamic types if true.
bool explicitVariant; bool explicitVariant;
union union {
{
/// ///
byte int8; byte int8;
/// ///
@ -171,117 +176,163 @@ struct DBusAny {
ubyte[] binaryData; ubyte[] binaryData;
} }
/// Manually creates a DBusAny object using a type, signature and implicit specifier. /++
Manually creates a DBusAny object using a type, signature and explicit
variant specifier.
Direct use of this constructor from user code should be avoided.
+/
this(int type, string signature, bool explicit) { this(int type, string signature, bool explicit) {
this.type = type; this.type = type;
this.signature = signature; this.signature = signature;
this.explicitVariant = explicit; this.explicitVariant = explicit;
} }
/// Automatically creates a DBusAny object with fitting parameters from a D type or Variant!T. /++
/// Pass a `Variant!T` to make this an explicit variant. Automatically creates a DBusAny object with fitting parameters from a D
type or Variant!T.
Pass a `Variant!T` to make this an explicit variant.
+/
this(T)(T value) { this(T)(T value) {
static if(is(T == byte) || is(T == ubyte)) { static if (is(T == byte) || is(T == ubyte)) {
this(typeCode!byte, null, false); this(typeCode!byte, null, false);
int8 = cast(byte) value; int8 = cast(byte) value;
} else static if(is(T == short)) { } else static if (is(T == short)) {
this(typeCode!short, null, false); this(typeCode!short, null, false);
int16 = cast(short) value; int16 = cast(short) value;
} else static if(is(T == ushort)) { } else static if (is(T == ushort)) {
this(typeCode!ushort, null, false); this(typeCode!ushort, null, false);
uint16 = cast(ushort) value; uint16 = cast(ushort) value;
} else static if(is(T == int)) { } else static if (is(T == int)) {
this(typeCode!int, null, false); this(typeCode!int, null, false);
int32 = cast(int) value; int32 = cast(int) value;
} else static if(is(T == uint)) { } else static if (is(T == uint)) {
this(typeCode!uint, null, false); this(typeCode!uint, null, false);
uint32 = cast(uint) value; uint32 = cast(uint) value;
} else static if(is(T == long)) { } else static if (is(T == long)) {
this(typeCode!long, null, false); this(typeCode!long, null, false);
int64 = cast(long) value; int64 = cast(long) value;
} else static if(is(T == ulong)) { } else static if (is(T == ulong)) {
this(typeCode!ulong, null, false); this(typeCode!ulong, null, false);
uint64 = cast(ulong) value; uint64 = cast(ulong) value;
} else static if(is(T == double)) { } else static if (is(T == double)) {
this(typeCode!double, null, false); this(typeCode!double, null, false);
float64 = cast(double) value; float64 = cast(double) value;
} else static if(isSomeString!T) { } else static if (isSomeString!T) {
this(typeCode!string, null, false); this(typeCode!string, null, false);
str = value.to!string; str = value.to!string;
} else static if(is(T == bool)) { } else static if (is(T == bool)) {
this(typeCode!bool, null, false); this(typeCode!bool, null, false);
boolean = cast(bool) value; boolean = cast(bool) value;
} else static if(is(T == ObjectPath)) { } else static if (is(T == ObjectPath)) {
this(typeCode!ObjectPath, null, false); this(typeCode!ObjectPath, null, false);
obj = value; obj = value;
} else static if(is(T == Variant!R, R)) { } else static if (is(T == Variant!R, R)) {
static if(is(R == DBusAny)) { static if (is(R == DBusAny)) {
type = value.data.type; type = value.data.type;
signature = value.data.signature; signature = value.data.signature;
explicitVariant = true; explicitVariant = true;
if(type == 'a' || type == 'r') { if (type == 'a' || type == 'r') {
if(signature == ['y']) if (signature == ['y']) {
binaryData = value.data.binaryData; binaryData = value.data.binaryData;
else } else {
array = value.data.array; array = value.data.array;
} else if(type == 's') }
} else if (type == 's') {
str = value.data.str; str = value.data.str;
else if(type == 'e') } else if (type == 'e') {
entry = value.data.entry; entry = value.data.entry;
else } else {
uint64 = value.data.uint64; uint64 = value.data.uint64;
}
} else { } else {
this(value.data); this(value.data);
explicitVariant = true; explicitVariant = true;
} }
} else static if(is(T : DictionaryEntry!(K, V), K, V)) { } else static if (is(T : DictionaryEntry!(K, V), K, V)) {
this('e', null, false); this('e', null, false);
entry = new DictionaryEntry!(DBusAny, DBusAny)(); entry = new DictionaryEntry!(DBusAny, DBusAny)();
static if(is(K == DBusAny)) static if (is(K == DBusAny)) {
entry.key = value.key; entry.key = value.key;
else } else {
entry.key = DBusAny(value.key); entry.key = DBusAny(value.key);
static if(is(V == DBusAny)) }
static if (is(V == DBusAny)) {
entry.value = value.value; entry.value = value.value;
else } else {
entry.value = DBusAny(value.value); entry.value = DBusAny(value.value);
} else static if(is(T == ubyte[]) || is(T == byte[])) { }
} else static if (is(T == ubyte[]) || is(T == byte[])) {
this('a', ['y'], false); this('a', ['y'], false);
binaryData = cast(ubyte[]) value; binaryData = cast(ubyte[]) value;
} else static if(isInputRange!T) { } else static if (isInputRange!T) {
this.type = 'a'; this.type = 'a';
static assert(!is(ElementType!T == DBusAny), "Array must consist of the same type, use Variant!DBusAny or DBusAny(tuple(...)) instead");
static assert(!is(ElementType!T == DBusAny),
"Array must consist of the same type, use Variant!DBusAny or DBusAny(tuple(...)) instead");
static assert(.typeSig!(ElementType!T) != "y"); static assert(.typeSig!(ElementType!T) != "y");
this.signature = .typeSig!(ElementType!T); this.signature = .typeSig!(ElementType!T);
this.explicitVariant = false; this.explicitVariant = false;
foreach(elem; value)
foreach (elem; value) {
array ~= DBusAny(elem); array ~= DBusAny(elem);
} else static if(isTuple!T) { }
} else static if (isTuple!T) {
this.type = 'r'; this.type = 'r';
this.signature = ['(']; this.signature = ['('];
this.explicitVariant = false; this.explicitVariant = false;
foreach(index, R; value.Types) {
foreach (index, R; value.Types) {
auto var = DBusAny(value[index]); auto var = DBusAny(value[index]);
tuple ~= var; tuple ~= var;
if(var.explicitVariant)
if (var.explicitVariant) {
this.signature ~= 'v'; this.signature ~= 'v';
else { } else {
if (var.type != 'r') if (var.type != 'r') {
this.signature ~= cast(char) var.type; this.signature ~= cast(char) var.type;
if(var.type == 'a' || var.type == 'r') }
if (var.type == 'a' || var.type == 'r') {
this.signature ~= var.signature; this.signature ~= var.signature;
}
}
}
this.signature ~= ')';
} else static if (is(T == struct) && canDBus!T) {
this.type = 'r';
this.signature = ['('];
this.explicitVariant = false;
foreach (index, R; Fields!T) {
static if (isAllowedField!(value.tupleof[index])) {
auto var = DBusAny(value.tupleof[index]);
tuple ~= var;
if (var.explicitVariant)
this.signature ~= 'v';
else {
if (var.type != 'r')
this.signature ~= cast(char) var.type;
if (var.type == 'a' || var.type == 'r')
this.signature ~= var.signature;
}
} }
} }
this.signature ~= ')'; this.signature ~= ')';
} else static if(isAssociativeArray!T) { } else static if (isAssociativeArray!T) {
this(value.byDictionaryEntries); this(value.byDictionaryEntries);
} else static assert(false, T.stringof ~ " not convertible to a Variant"); } else {
static assert(false, T.stringof ~ " not convertible to a Variant");
}
} }
/// ///
string toString() const { string toString() const {
string valueStr; string valueStr;
switch(type) { switch (type) {
case typeCode!byte: case typeCode!byte:
valueStr = int8.to!string; valueStr = int8.to!string;
break; break;
@ -318,10 +369,12 @@ struct DBusAny {
case 'a': case 'a':
import std.digest.digest : toHexString; import std.digest.digest : toHexString;
if(signature == ['y']) if (signature == ['y']) {
valueStr = "binary(" ~ binaryData.toHexString ~ ')'; valueStr = "binary(" ~ binaryData.toHexString ~ ')';
else } else {
valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']'; valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']';
}
break; break;
case 'r': case 'r':
valueStr = '(' ~ tuple.map!(a => a.toString).join(", ") ~ ')'; valueStr = '(' ~ tuple.map!(a => a.toString).join(", ") ~ ')';
@ -333,10 +386,9 @@ struct DBusAny {
valueStr = "unknown"; valueStr = "unknown";
break; break;
} }
return "DBusAny(" ~ cast(char) type
~ ", \"" ~ signature.idup return "DBusAny(" ~ cast(char) type ~ ", \"" ~ signature.idup ~ "\", " ~ (explicitVariant
~ "\", " ~ (explicitVariant ? "explicit" : "implicit") ? "explicit" : "implicit") ~ ", " ~ valueStr ~ ")";
~ ", " ~ valueStr ~ ")";
} }
/++ /++
@ -354,24 +406,21 @@ struct DBusAny {
DBusAny object is not the same as the DBus type used to represent T. DBusAny object is not the same as the DBus type used to represent T.
+/ +/
T get(T)() @property const T get(T)() @property const
if(staticIndexOf!(T, BasicTypes) >= 0) if (staticIndexOf!(T, BasicTypes) >= 0) {
{ enforce(type == typeCode!T, new TypeMismatchException(
enforce(type == typeCode!T, "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.",
new TypeMismatchException( typeCode!T, type));
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
~ " a value of DBus type '" ~ typeSig ~ "'.", typeCode!T, type));
static if(isIntegral!T) { static if (isIntegral!T) {
enum memberName = enum memberName = (isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
(isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
return __traits(getMember, this, memberName); return __traits(getMember, this, memberName);
} else static if(is(T == double)) { } else static if (is(T == double)) {
return float64; return float64;
} else static if(is(T == string)) { } else static if (is(T == string)) {
return str; return str;
} else static if(is(T == ObjectPath)) { } else static if (is(T == ObjectPath)) {
return obj; return obj;
} else static if(is(T == bool)) { } else static if (is(T == bool)) {
return boolean; return boolean;
} else { } else {
static assert(false); static assert(false);
@ -380,38 +429,34 @@ struct DBusAny {
/// ditto /// ditto
T get(T)() @property const T get(T)() @property const
if(is(T == const(DBusAny)[])) if (is(T == const(DBusAny)[])) {
{ enforce((type == 'a' && signature != "y") || type == 'r', new TypeMismatchException(
enforce((type == 'a' && signature != "y") || type == 'r', "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.",
new TypeMismatchException( 'a', type));
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
~ " a value of DBus type '" ~ this.typeSig ~ "'.",
typeCode!T, type));
return array; return array;
} }
/// ditto /// ditto
T get(T)() @property const T get(T)() @property const
if (is(T == const(ubyte)[])) if (is(T == const(ubyte)[])) {
{ enforce(type == 'a' && signature == "y", new TypeMismatchException(
enforce(type == 'a' && signature == "y", "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.",
new TypeMismatchException( 'a', type));
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
~ " a value of DBus type '" ~ this.typeSig ~ "'.",
typeCode!T, type));
return binaryData; return binaryData;
} }
/// If the value is an array of DictionaryEntries this will return a HashMap /// If the value is an array of DictionaryEntries this will return a HashMap
DBusAny[DBusAny] toAA() { deprecated("Please use to!(V[K])") DBusAny[DBusAny] toAA() {
enforce(type == 'a' && signature && signature[0] == '{'); enforce(type == 'a' && signature && signature[0] == '{');
DBusAny[DBusAny] aa; DBusAny[DBusAny] aa;
foreach(val; array) {
foreach (val; array) {
enforce(val.type == 'e'); enforce(val.type == 'e');
aa[val.entry.key] = val.entry.value; aa[val.entry.key] = val.entry.value;
} }
return aa; return aa;
} }
@ -421,126 +466,162 @@ struct DBusAny {
Returns: Returns:
The type signature of the value stored in this DBusAny object. The type signature of the value stored in this DBusAny object.
+/ +/
string typeSig() @property const pure nothrow @safe string typeSig() @property const pure nothrow @safe {
{ if (type == 'a') {
if(type == 'a') {
return "a" ~ signature; return "a" ~ signature;
} else if(type == 'r') { } else if (type == 'r') {
return signature; return signature;
} else if(type == 'e') { } else if (type == 'e') {
return () @trusted { return () @trusted{
return "{" ~ entry.key.signature ~ entry.value.signature ~ "}"; return "{" ~ entry.key.signature ~ entry.value.signature ~ "}";
} (); }();
} else { } else {
return [ cast(char) type ]; return [cast(char) type];
} }
} }
/// Converts a basic type, a tuple or an array to the D type with type checking. Tuples can get converted to an array too. /++
T to(T)() { Converts a basic type, a tuple or an array to the D type with type checking.
static if(is(T == Variant!R, R)) {
static if(is(R == DBusAny)) { Tuples can be converted to an array of DBusAny, but not to any other array.
+/
T to(T)() @property const pure {
// Just use `get` if possible
static if (canDBus!T && __traits(compiles, get!T)) {
if (this.typeSig == .typeSig!T)
return get!T;
}
// If we get here, we need some type conversion
static if (is(T == Variant!R, R)) {
static if (is(R == DBusAny)) {
auto v = to!R; auto v = to!R;
v.explicitVariant = false; v.explicitVariant = false;
return Variant!R(v); return Variant!R(v);
} else } else {
return Variant!R(to!R); return Variant!R(to!R);
} else static if(is(T == DBusAny)) { }
} else static if (is(T == DBusAny)) {
return this; return this;
} else static if(isIntegral!T || isFloatingPoint!T) { } else {
switch(type) { // In here are all static if blocks that may fall through to the throw
case typeCode!byte: // statement at the bottom of this block.
return cast(T) int8;
case typeCode!short: static if (is(T == DictionaryEntry!(K, V), K, V)) {
return cast(T) int16; if (type == 'e') {
case typeCode!ushort: static if (is(T == typeof(entry))) {
return cast(T) uint16; return entry;
case typeCode!int: } else {
return cast(T) int32; return DictionaryEntry(entry.key.to!K, entry.value.to!V);
case typeCode!uint: }
return cast(T) uint32; }
case typeCode!long: } else static if (isAssociativeArray!T) {
return cast(T) int64; if (type == 'a' && (!array.length || array[0].type == 'e')) {
case typeCode!ulong: alias K = Unqual!(KeyType!T);
return cast(T) uint64; alias V = Unqual!(ValueType!T);
case typeCode!double: V[K] ret;
return cast(T) float64;
default: foreach (pair; array) {
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); assert(pair.type == 'e');
ret[pair.entry.key.to!K] = pair.entry.value.to!V;
}
return cast(T) ret;
}
} else static if (isDynamicArray!T && !isSomeString!T) {
alias E = Unqual!(ElementType!T);
if (typeSig == "ay") {
auto data = get!(const(ubyte)[]);
static if (is(E == ubyte) || is(E == byte)) {
return cast(T) data.dup;
} else {
return cast(T) data.map!(elem => elem.to!E).array;
}
} else if (type == 'a' || (type == 'r' && is(E == DBusAny))) {
return cast(T) get!(const(DBusAny)[]).map!(elem => elem.to!E).array;
}
} else static if (isTuple!T) {
if (type == 'r') {
T ret;
foreach (i, T; ret.Types) {
ret[i] = tuple[i].to!T;
}
return ret;
}
} else static if (is(T == struct) && canDBus!T) {
if (type == 'r') {
T ret;
size_t j;
foreach (i, F; Fields!T) {
static if (isAllowedField!(ret.tupleof[i])) {
ret.tupleof[i] = tuple[j++].to!F;
}
}
return ret;
}
} else {
alias isPreciselyConvertible = ApplyRight!(isImplicitlyConvertible, T);
template isUnpreciselyConvertible(S) {
enum isUnpreciselyConvertible = !isPreciselyConvertible!S
&& __traits(compiles, get!S.to!T);
}
// Try to be precise
foreach (B; Filter!(isPreciselyConvertible, BasicTypes)) {
if (type == typeCode!B)
return get!B;
}
// Try to convert
foreach (B; Filter!(isUnpreciselyConvertible, BasicTypes)) {
if (type == typeCode!B)
return get!B.to!T;
}
} }
} else static if(is(T == bool)) {
if(type == 'b') throw new ConvException("Cannot convert from DBus type '" ~ this.typeSig ~ "' to "
return boolean; ~ T.stringof);
else }
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
} else static if(isSomeString!T) {
if(type == 's')
return str.to!T;
else if(type == 'o')
return obj.toString();
else
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
} else static if(is(T == ObjectPath)) {
if(type == 'o')
return obj;
else
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
} else static if(isDynamicArray!T) {
if(type != 'a' && type != 'r')
throw new Exception("Can't convert type " ~ cast(char) type ~ " to an array");
T ret;
if(signature == ['y']) {
static if(isIntegral!(ElementType!T))
foreach(elem; binaryData)
ret ~= elem.to!(ElementType!T);
} else
foreach(elem; array)
ret ~= elem.to!(ElementType!T);
return ret;
} else static if(isTuple!T) {
if(type != 'r')
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
T ret;
enforce(ret.Types.length == tuple.length, "Tuple length mismatch");
foreach(index, T; ret.Types)
ret[index] = tuple[index].to!T;
return ret;
} else static if(isAssociativeArray!T) {
if(type != 'a' || !signature || signature[0] != '{')
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
T ret;
foreach(pair; array) {
enforce(pair.type == 'e');
ret[pair.entry.key.to!(KeyType!T)] = pair.entry.value.to!(ValueType!T);
}
return ret;
} else static assert(false, "Can't convert variant to " ~ T.stringof);
} }
bool opEquals(ref in DBusAny b) const { bool opEquals(ref in DBusAny b) const {
if(b.type != type || b.explicitVariant != explicitVariant) if (b.type != type || b.explicitVariant != explicitVariant) {
return false; return false;
if((type == 'a' || type == 'r') && b.signature != signature) }
if ((type == 'a' || type == 'r') && b.signature != signature) {
return false; return false;
if(type == 'a' && signature == ['y']) }
if (type == 'a' && signature == ['y']) {
return binaryData == b.binaryData; return binaryData == b.binaryData;
if(type == 'a') }
if (type == 'a') {
return array == b.array; return array == b.array;
else if(type == 'r') } else if (type == 'r') {
return tuple == b.tuple; return tuple == b.tuple;
else if(type == 's') } else if (type == 's') {
return str == b.str; return str == b.str;
else if(type == 'o') } else if (type == 'o') {
return obj == b.obj; return obj == b.obj;
else if(type == 'e') } else if (type == 'e') {
return entry == b.entry || (entry && b.entry && *entry == *b.entry); return entry == b.entry || (entry && b.entry && *entry == *b.entry);
else } else {
return uint64 == b.uint64; return uint64 == b.uint64;
}
} }
} }
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
DBusAny set(string member, T)(DBusAny v, T value) { DBusAny set(string member, T)(DBusAny v, T value) {
mixin("v." ~ member ~ " = value;"); mixin("v." ~ member ~ " = value;");
return v; return v;
@ -548,15 +629,6 @@ unittest {
void test(T)(T value, DBusAny b) { void test(T)(T value, DBusAny b) {
assertEqual(DBusAny(value), b); assertEqual(DBusAny(value), b);
static if(is(T == Variant!R, R)) {
static if(__traits(compiles, b.get!R))
assertEqual(b.get!R, value.data);
} else {
static if(__traits(compiles, b.get!T))
assertEqual(b.get!T, value);
}
assertEqual(b.to!T, value); assertEqual(b.to!T, value);
b.toString(); b.toString();
} }
@ -568,8 +640,12 @@ unittest {
test(cast(uint) 184, set!"uint32"(DBusAny('u', null, false), cast(uint) 184)); test(cast(uint) 184, set!"uint32"(DBusAny('u', null, false), cast(uint) 184));
test(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 184)); test(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 184));
test(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184)); test(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184));
test(1.84, set!"float64"(DBusAny('d', null, false), 1.84));
test(true, set!"boolean"(DBusAny('b', null, false), true)); test(true, set!"boolean"(DBusAny('b', null, false), true));
test(cast(ubyte[]) [1, 2, 3], set!"binaryData"(DBusAny('a', ['y'], false), cast(ubyte[]) [1, 2, 3])); test("abc", set!"str"(DBusAny('s', null, false), "abc"));
test(ObjectPath("/foo/Bar"), set!"obj"(DBusAny('o', null, false), ObjectPath("/foo/Bar")));
test(cast(ubyte[])[1, 2, 3], set!"binaryData"(DBusAny('a', ['y'], false),
cast(ubyte[])[1, 2, 3]));
test(variant(cast(ubyte) 184), set!"int8"(DBusAny('y', null, true), cast(byte) 184)); test(variant(cast(ubyte) 184), set!"int8"(DBusAny('y', null, true), cast(byte) 184));
test(variant(cast(short) 184), set!"int16"(DBusAny('n', null, true), cast(short) 184)); test(variant(cast(short) 184), set!"int16"(DBusAny('n', null, true), cast(short) 184));
@ -578,19 +654,29 @@ unittest {
test(variant(cast(uint) 184), set!"uint32"(DBusAny('u', null, true), cast(uint) 184)); test(variant(cast(uint) 184), set!"uint32"(DBusAny('u', null, true), cast(uint) 184));
test(variant(cast(long) 184), set!"int64"(DBusAny('x', null, true), cast(long) 184)); test(variant(cast(long) 184), set!"int64"(DBusAny('x', null, true), cast(long) 184));
test(variant(cast(ulong) 184), set!"uint64"(DBusAny('t', null, true), cast(ulong) 184)); test(variant(cast(ulong) 184), set!"uint64"(DBusAny('t', null, true), cast(ulong) 184));
test(variant(1.84), set!"float64"(DBusAny('d', null, true), 1.84));
test(variant(true), set!"boolean"(DBusAny('b', null, true), true)); test(variant(true), set!"boolean"(DBusAny('b', null, true), true));
test(variant(cast(ubyte[]) [1, 2, 3]), set!"binaryData"(DBusAny('a', ['y'], true), cast(ubyte[]) [1, 2, 3])); test(variant("abc"), set!"str"(DBusAny('s', null, true), "abc"));
test(variant(ObjectPath("/foo/Bar")), set!"obj"(DBusAny('o', null, true),
ObjectPath("/foo/Bar")));
test(variant(cast(ubyte[])[1, 2, 3]), set!"binaryData"(DBusAny('a', ['y'],
true), cast(ubyte[])[1, 2, 3]));
test(variant(DBusAny(5)), set!"int32"(DBusAny('i', null, true), 5)); test(variant(DBusAny(5)), set!"int32"(DBusAny('i', null, true), 5));
test([1, 2, 3], set!"array"(DBusAny('a', ['i'], false), [DBusAny(1), DBusAny(2), DBusAny(3)])); test([1, 2, 3], set!"array"(DBusAny('a', ['i'], false), [DBusAny(1), DBusAny(2), DBusAny(3)]));
test(variant([1, 2, 3]), set!"array"(DBusAny('a', ['i'], true), [DBusAny(1), DBusAny(2), DBusAny(3)])); test(variant([1, 2, 3]), set!"array"(DBusAny('a', ['i'], true), [DBusAny(1),
DBusAny(2), DBusAny(3)]));
test(tuple("a", 4, [1, 2]), set!"tuple"(DBusAny('r', "(siai)".dup, false), [DBusAny("a"), DBusAny(4), DBusAny([1, 2])])); test(tuple("a", 4, [1, 2]), set!"tuple"(DBusAny('r', "(siai)".dup, false),
test(tuple("a", variant(4), variant([1, 2])), set!"tuple"(DBusAny('r', "(svv)", false), [DBusAny("a"), DBusAny(variant(4)), DBusAny(variant([1, 2]))])); [DBusAny("a"), DBusAny(4), DBusAny([1, 2])]));
test(tuple("a", variant(4), variant([1, 2])), set!"tuple"(DBusAny('r',
"(svv)", false), [DBusAny("a"), DBusAny(variant(4)), DBusAny(variant([1, 2]))]));
test(["a": "b"], set!"array"(DBusAny('a', "{ss}", false), [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny("a"), DBusAny("b")))])); test(["a" : "b"], set!"array"(DBusAny('a', "{ss}", false),
test([variant("a"): 4], set!"array"(DBusAny('a', "{vi}", false), [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny(variant("a")), DBusAny(4)))])); [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny("a"), DBusAny("b")))]));
test([variant("a") : 4], set!"array"(DBusAny('a', "{vi}", false),
[DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny(variant("a")), DBusAny(4)))]));
} }
/// Marks the data as variant on serialization /// Marks the data as variant on serialization
@ -605,17 +691,21 @@ Variant!T variant(T)(T data) {
enum MessageType { enum MessageType {
Invalid = 0, Invalid = 0,
Call, Return, Error, Signal Call,
Return,
Error,
Signal
} }
struct Message { struct Message {
DBusMessage *msg; DBusMessage* msg;
this(string dest, string path, string iface, string method) { this(string dest, string path, string iface, string method) {
msg = dbus_message_new_method_call(dest.toStringz(), path.toStringz(), iface.toStringz(), method.toStringz()); msg = dbus_message_new_method_call(dest.toStringz(), path.toStringz(),
iface.toStringz(), method.toStringz());
} }
this(DBusMessage *m) { this(DBusMessage* m) {
msg = m; msg = m;
} }
@ -627,7 +717,8 @@ struct Message {
dbus_message_unref(msg); dbus_message_unref(msg);
} }
void build(TS...)(TS args) if(allCanDBus!TS) { void build(TS...)(TS args)
if (allCanDBus!TS) {
DBusMessageIter iter; DBusMessageIter iter;
dbus_message_iter_init_append(msg, &iter); dbus_message_iter_init_append(msg, &iter);
buildIter(&iter, args); buildIter(&iter, args);
@ -639,14 +730,17 @@ struct Message {
read the first argument. This is suitable for single item returns. read the first argument. This is suitable for single item returns.
To read multiple arguments use readTuple. To read multiple arguments use readTuple.
*/ */
T read(T)() if(canDBus!T) { T read(T)()
if (canDBus!T) {
DBusMessageIter iter; DBusMessageIter iter;
dbus_message_iter_init(msg, &iter); dbus_message_iter_init(msg, &iter);
return readIter!T(&iter); return readIter!T(&iter);
} }
alias read to; alias read to;
Tup readTuple(Tup)() if(isTuple!Tup && allCanDBus!(Tup.Types)) { Tup readTuple(Tup)()
if (isTuple!Tup && allCanDBus!(Tup.Types)) {
DBusMessageIter iter; DBusMessageIter iter;
dbus_message_iter_init(msg, &iter); dbus_message_iter_init(msg, &iter);
Tup ret; Tup ret;
@ -659,7 +753,7 @@ struct Message {
} }
MessageType type() { MessageType type() {
return cast(MessageType)dbus_message_get_type(msg); return cast(MessageType) dbus_message_get_type(msg);
} }
bool isCall() { bool isCall() {
@ -673,21 +767,25 @@ struct Message {
assert(cStr != null); assert(cStr != null);
return cStr.fromStringz().idup; return cStr.fromStringz().idup;
} }
string path() { string path() {
const(char)* cStr = dbus_message_get_path(msg); const(char)* cStr = dbus_message_get_path(msg);
assert(cStr != null); assert(cStr != null);
return cStr.fromStringz().idup; return cStr.fromStringz().idup;
} }
string iface() { string iface() {
const(char)* cStr = dbus_message_get_interface(msg); const(char)* cStr = dbus_message_get_interface(msg);
assert(cStr != null); assert(cStr != null);
return cStr.fromStringz().idup; return cStr.fromStringz().idup;
} }
string member() { string member() {
const(char)* cStr = dbus_message_get_member(msg); const(char)* cStr = dbus_message_get_member(msg);
assert(cStr != null); assert(cStr != null);
return cStr.fromStringz().idup; return cStr.fromStringz().idup;
} }
string sender() { string sender() {
const(char)* cStr = dbus_message_get_sender(msg); const(char)* cStr = dbus_message_get_sender(msg);
assert(cStr != null); assert(cStr != null);
@ -697,13 +795,14 @@ struct Message {
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
auto msg = Message("org.example.test", "/test","org.example.testing","testMethod");
auto msg = Message("org.example.test", "/test", "org.example.testing", "testMethod");
msg.path().assertEqual("/test"); msg.path().assertEqual("/test");
} }
struct Connection { struct Connection {
DBusConnection *conn; DBusConnection* conn;
this(DBusConnection *connection) { this(DBusConnection* connection) {
conn = connection; conn = connection;
} }
@ -720,7 +819,7 @@ struct Connection {
} }
void send(Message msg) { void send(Message msg) {
dbus_connection_send(conn,msg.msg, null); dbus_connection_send(conn, msg.msg, null);
} }
void sendBlocking(Message msg) { void sendBlocking(Message msg) {
@ -729,13 +828,13 @@ struct Connection {
} }
Message sendWithReplyBlocking(Message msg, int timeout = -1) { Message sendWithReplyBlocking(Message msg, int timeout = -1) {
DBusMessage *dbusMsg = msg.msg; DBusMessage* dbusMsg = msg.msg;
dbus_message_ref(dbusMsg); dbus_message_ref(dbusMsg);
DBusMessage *reply = wrapErrors((err) { DBusMessage* reply = wrapErrors((err) {
auto ret = dbus_connection_send_with_reply_and_block(conn,dbusMsg,timeout,err); auto ret = dbus_connection_send_with_reply_and_block(conn, dbusMsg, timeout, err);
dbus_message_unref(dbusMsg); dbus_message_unref(dbusMsg);
return ret; return ret;
}); });
return Message(reply); return Message(reply);
} }
@ -758,7 +857,7 @@ unittest {
struct S2 { struct S2 {
int h, i; int h, i;
@(Yes.DBusMarshal) int j, k; @(Yes.DBusMarshal) int j, k;
int *p; int* p;
} }
@dbusMarshaling(MarshalingFlag.includePrivateFields) @dbusMarshaling(MarshalingFlag.includePrivateFields)
@ -774,40 +873,34 @@ unittest {
__gshared int dummy; __gshared int dummy;
enum testStruct = S3( enum testStruct = S3(variant(5), "blah", S1(-7, 63.5, "test"), S2(84, -123,
variant(5), "blah", 78, 432, &dummy), 16);
S1(-7, 63.5, "test"),
S2(84, -123, 78, 432, &dummy),
16
);
msg.build(testStruct);
// Non-marshaled fields should appear as freshly initialized // Non-marshaled fields should appear as freshly initialized
enum expectedResult = S3( enum expectedResult = S3(variant(5), "blah", S1(int.init, 63.5, "test"),
variant(5), "blah", S2(int.init, int.init, 78, 432, null), uint.init);
S1(int.init, 63.5, "test"),
S2(int.init, int.init, 78, 432, null),
uint.init
);
// Test struct conversion in building/reading messages
msg.build(testStruct);
msg.read!S3().assertEqual(expectedResult); msg.read!S3().assertEqual(expectedResult);
// Test struct conversion in DBusAny
DBusAny(testStruct).to!S3.assertEqual(expectedResult);
} }
Connection connectToBus(DBusBusType bus = DBusBusType.DBUS_BUS_SESSION) { Connection connectToBus(DBusBusType bus = DBusBusType.DBUS_BUS_SESSION) {
DBusConnection *conn = wrapErrors((err) { return dbus_bus_get(bus,err); }); DBusConnection* conn = wrapErrors((err) { return dbus_bus_get(bus, err); });
return Connection(conn); return Connection(conn);
} }
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
// This test will only pass if DBus is installed. // This test will only pass if DBus is installed.
Connection conn = connectToBus(); Connection conn = connectToBus();
conn.conn.assertTruthy(); conn.conn.assertTruthy();
// We can only count on no system bus on OSX // We can only count on no system bus on OSX
version(OSX) { version (OSX) {
connectToBus(DBusBusType.DBUS_BUS_SYSTEM).assertThrow!DBusException(); connectToBus(DBusBusType.DBUS_BUS_SYSTEM).assertThrow!DBusException();
} }
} }

View file

@ -26,12 +26,10 @@ auto byDictionaryEntries(K, V)(V[K] aa) {
Its meaning became unclear when support for Phobos-style variants was added. Its meaning became unclear when support for Phobos-style variants was added.
It seemed best to remove it at that point. It seemed best to remove it at that point.
+/ +/
deprecated("Use std.traits.isInstanceOf instead.") deprecated("Use std.traits.isInstanceOf instead.") template isVariant(T) {
template isVariant(T) { static if (isBasicType!T || isInputRange!T) {
static if(isBasicType!T || isInputRange!T) {
enum isVariant = false; enum isVariant = false;
} else static if(__traits(compiles, TemplateOf!T) } else static if (__traits(compiles, TemplateOf!T) && __traits(isSame, TemplateOf!T, Variant)) {
&& __traits(isSame, TemplateOf!T, Variant)) {
enum isVariant = true; enum isVariant = true;
} else { } else {
enum isVariant = false; enum isVariant = false;
@ -45,37 +43,26 @@ template VariantType(T) {
template allCanDBus(TS...) { template allCanDBus(TS...) {
static if (TS.length == 0) { static if (TS.length == 0) {
enum allCanDBus = true; enum allCanDBus = true;
} else static if(!canDBus!(TS[0])) { } else static if (!canDBus!(TS[0])) {
enum allCanDBus = false; enum allCanDBus = false;
} else { } else {
enum allCanDBus = allCanDBus!(TS[1..$]); enum allCanDBus = allCanDBus!(TS[1 .. $]);
} }
} }
/++ /++
AliasSeq of all basic types in terms of the DBus typesystem AliasSeq of all basic types in terms of the DBus typesystem
+/ +/
package // Don't add to the API yet, 'cause I intend to move it later package // Don't add to the API yet, 'cause I intend to move it later
alias BasicTypes = AliasSeq!( alias BasicTypes = AliasSeq!(bool, byte, short, ushort, int, uint, long, ulong,
bool, double, string, ObjectPath);
byte,
short,
ushort,
int,
uint,
long,
ulong,
double,
string,
ObjectPath
);
template basicDBus(T) { template basicDBus(T) {
static if(staticIndexOf!(T, BasicTypes) >= 0) { static if (staticIndexOf!(T, BasicTypes) >= 0) {
enum basicDBus = true; enum basicDBus = true;
} else static if(is(T B == enum)) { } else static if (is(T B == enum)) {
enum basicDBus = basicDBus!B; enum basicDBus = basicDBus!B;
} else static if(isInstanceOf!(BitFlags, T)) { } else static if (isInstanceOf!(BitFlags, T)) {
alias TemplateArgsOf!T[0] E; alias TemplateArgsOf!T[0] E;
enum basicDBus = basicDBus!E; enum basicDBus = basicDBus!E;
} else { } else {
@ -84,24 +71,24 @@ template basicDBus(T) {
} }
template canDBus(T) { template canDBus(T) {
static if(basicDBus!T || is(T == DBusAny)) { static if (basicDBus!T || is(T == DBusAny)) {
enum canDBus = true; enum canDBus = true;
} else static if(isInstanceOf!(Variant, T)) { } else static if (isInstanceOf!(Variant, T)) {
enum canDBus = canDBus!(VariantType!T); enum canDBus = canDBus!(VariantType!T);
} else static if(isInstanceOf!(VariantN, T)) { } else static if (isInstanceOf!(VariantN, T)) {
// Phobos-style variants are supported if limited to DBus compatible types. // Phobos-style variants are supported if limited to DBus compatible types.
enum canDBus = (T.AllowedTypes.length > 0) && allCanDBus!(T.AllowedTypes); enum canDBus = (T.AllowedTypes.length > 0) && allCanDBus!(T.AllowedTypes);
} else static if(isTuple!T) { } else static if (isTuple!T) {
enum canDBus = allCanDBus!(T.Types); enum canDBus = allCanDBus!(T.Types);
} else static if(isInputRange!T) { } else static if (isInputRange!T) {
static if(is(ElementType!T == DictionaryEntry!(K, V), K, V)) { static if (is(ElementType!T == DictionaryEntry!(K, V), K, V)) {
enum canDBus = basicDBus!K && canDBus!V; enum canDBus = basicDBus!K && canDBus!V;
} else { } else {
enum canDBus = canDBus!(ElementType!T); enum canDBus = canDBus!(ElementType!T);
} }
} else static if(isAssociativeArray!T) { } else static if (isAssociativeArray!T) {
enum canDBus = basicDBus!(KeyType!T) && canDBus!(ValueType!T); enum canDBus = basicDBus!(KeyType!T) && canDBus!(ValueType!T);
} else static if(is(T == struct) && !isInstanceOf!(DictionaryEntry, T)) { } else static if (is(T == struct) && !isInstanceOf!(DictionaryEntry, T)) {
enum canDBus = allCanDBus!(AllowedFieldTypes!T); enum canDBus = allCanDBus!(AllowedFieldTypes!T);
} else { } else {
enum canDBus = false; enum canDBus = false;
@ -110,60 +97,63 @@ template canDBus(T) {
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
(canDBus!int).assertTrue(); (canDBus!int).assertTrue();
(canDBus!(int[])).assertTrue(); (canDBus!(int[])).assertTrue();
(allCanDBus!(int,string,bool)).assertTrue(); (allCanDBus!(int, string, bool)).assertTrue();
(canDBus!(Tuple!(int[],bool,Variant!short))).assertTrue(); (canDBus!(Tuple!(int[], bool, Variant!short))).assertTrue();
(canDBus!(Tuple!(int[],int[string]))).assertTrue(); (canDBus!(Tuple!(int[], int[string]))).assertTrue();
(canDBus!(int[string])).assertTrue(); (canDBus!(int[string])).assertTrue();
} }
string typeSig(T)() if(canDBus!T) { string typeSig(T)()
static if(is(T == byte)) { if (canDBus!T) {
static if (is(T == byte)) {
return "y"; return "y";
} else static if(is(T == bool)) { } else static if (is(T == bool)) {
return "b"; return "b";
} else static if(is(T == short)) { } else static if (is(T == short)) {
return "n"; return "n";
} else static if(is(T == ushort)) { } else static if (is(T == ushort)) {
return "q"; return "q";
} else static if(is(T == int)) { } else static if (is(T == int)) {
return "i"; return "i";
} else static if(is(T == uint)) { } else static if (is(T == uint)) {
return "u"; return "u";
} else static if(is(T == long)) { } else static if (is(T == long)) {
return "x"; return "x";
} else static if(is(T == ulong)) { } else static if (is(T == ulong)) {
return "t"; return "t";
} else static if(is(T == double)) { } else static if (is(T == double)) {
return "d"; return "d";
} else static if(is(T == string)) { } else static if (is(T == string)) {
return "s"; return "s";
} else static if(is(T == ObjectPath)) { } else static if (is(T == ObjectPath)) {
return "o"; return "o";
} else static if(isInstanceOf!(Variant, T) || isInstanceOf!(VariantN, T)) { } else static if (isInstanceOf!(Variant, T) || isInstanceOf!(VariantN, T)) {
return "v"; return "v";
} else static if(is(T B == enum)) { } else static if (is(T B == enum)) {
return typeSig!B; return typeSig!B;
} else static if(isInstanceOf!(BitFlags, T)) { } else static if (isInstanceOf!(BitFlags, T)) {
alias TemplateArgsOf!T[0] E; alias TemplateArgsOf!T[0] E;
return typeSig!E; return typeSig!E;
} else static if(is(T == DBusAny)) { } else static if (is(T == DBusAny)) {
static assert(false, "Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired."); static assert(false,
} else static if(isTuple!T) { "Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired.");
} else static if (isTuple!T) {
string sig = "("; string sig = "(";
foreach(i, S; T.Types) { foreach (i, S; T.Types) {
sig ~= typeSig!S(); sig ~= typeSig!S();
} }
sig ~= ")"; sig ~= ")";
return sig; return sig;
} else static if(isInputRange!T) { } else static if (isInputRange!T) {
return "a" ~ typeSig!(ElementType!T)(); return "a" ~ typeSig!(ElementType!T)();
} else static if(isAssociativeArray!T) { } else static if (isAssociativeArray!T) {
return "a{" ~ typeSig!(KeyType!T) ~ typeSig!(ValueType!T) ~ "}"; return "a{" ~ typeSig!(KeyType!T) ~ typeSig!(ValueType!T) ~ "}";
} else static if(is(T == struct)) { } else static if (is(T == struct)) {
string sig = "("; string sig = "(";
foreach(i, S; AllowedFieldTypes!T) { foreach (i, S; AllowedFieldTypes!T) {
sig ~= typeSig!S(); sig ~= typeSig!S();
} }
sig ~= ")"; sig ~= ")";
@ -171,66 +161,99 @@ string typeSig(T)() if(canDBus!T) {
} }
} }
string typeSig(T)() if(isInstanceOf!(DictionaryEntry, T)) { string typeSig(T)()
if (isInstanceOf!(DictionaryEntry, T)) {
alias typeof(T.key) K; alias typeof(T.key) K;
alias typeof(T.value) V; alias typeof(T.value) V;
return "{" ~ typeSig!K ~ typeSig!V ~ '}'; return "{" ~ typeSig!K ~ typeSig!V ~ '}';
} }
string[] typeSigReturn(T)() if(canDBus!T) { string[] typeSigReturn(T)()
static if(is(T == Tuple!TS, TS...)) if (canDBus!T) {
static if (is(T == Tuple!TS, TS...))
return typeSigArr!TS; return typeSigArr!TS;
else else
return [typeSig!T]; return [typeSig!T];
} }
string typeSigAll(TS...)() if(allCanDBus!TS) { string typeSigAll(TS...)()
if (allCanDBus!TS) {
string sig = ""; string sig = "";
foreach(i,T; TS) { foreach (i, T; TS) {
sig ~= typeSig!T(); sig ~= typeSig!T();
} }
return sig; return sig;
} }
string[] typeSigArr(TS...)() if(allCanDBus!TS) { string[] typeSigArr(TS...)()
if (allCanDBus!TS) {
string[] sig = []; string[] sig = [];
foreach(i,T; TS) { foreach (i, T; TS) {
sig ~= typeSig!T(); sig ~= typeSig!T();
} }
return sig; return sig;
} }
int typeCode(T)() if(canDBus!T) { int typeCode(T)()
if (canDBus!T) {
int code = typeSig!T()[0]; int code = typeSig!T()[0];
return (code != '(') ? code : 'r'; return (code != '(') ? code : 'r';
} }
int typeCode(T)() if(isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) { int typeCode(T)()
if (isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) {
return 'e'; return 'e';
} }
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
// basics // basics
typeSig!int().assertEqual("i"); typeSig!int().assertEqual("i");
typeSig!bool().assertEqual("b"); typeSig!bool().assertEqual("b");
typeSig!string().assertEqual("s"); typeSig!string().assertEqual("s");
typeSig!(Variant!int)().assertEqual("v"); typeSig!(Variant!int)().assertEqual("v");
// enums // enums
enum E : byte { a, b, c } enum E : byte {
a,
b,
c
}
typeSig!E().assertEqual(typeSig!byte()); typeSig!E().assertEqual(typeSig!byte());
enum U : string { One = "One", Two = "Two" } enum U : string {
One = "One",
Two = "Two"
}
typeSig!U().assertEqual(typeSig!string()); typeSig!U().assertEqual(typeSig!string());
// bit flags // bit flags
enum F : uint { a = 1, b = 2, c = 4 } enum F : uint {
a = 1,
b = 2,
c = 4
}
typeSig!(BitFlags!F)().assertEqual(typeSig!uint()); typeSig!(BitFlags!F)().assertEqual(typeSig!uint());
// tuples (represented as structs in DBus) // tuples (represented as structs in DBus)
typeSig!(Tuple!(int,string,string)).assertEqual("(iss)"); typeSig!(Tuple!(int, string, string)).assertEqual("(iss)");
typeSig!(Tuple!(int,string,Variant!int,Tuple!(int,"k",double,"x"))).assertEqual("(isv(id))"); typeSig!(Tuple!(int, string, Variant!int, Tuple!(int, "k", double, "x"))).assertEqual(
"(isv(id))");
// structs // structs
struct S1 { int a; double b; string s; } struct S1 {
int a;
double b;
string s;
}
typeSig!S1.assertEqual("(ids)"); typeSig!S1.assertEqual("(ids)");
struct S2 { Variant!int c; string d; S1 e; uint f; } struct S2 {
Variant!int c;
string d;
S1 e;
uint f;
}
typeSig!S2.assertEqual("(vs(ids)u)"); typeSig!S2.assertEqual("(vs(ids)u)");
// arrays // arrays
typeSig!(int[]).assertEqual("ai"); typeSig!(int[]).assertEqual("ai");
@ -240,7 +263,7 @@ unittest {
typeSig!(int[string]).assertEqual("a{si}"); typeSig!(int[string]).assertEqual("a{si}");
typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}"); typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}");
// multiple arguments // multiple arguments
typeSigAll!(int,bool).assertEqual("ib"); typeSigAll!(int, bool).assertEqual("ib");
// Phobos-style variants // Phobos-style variants
canDBus!(std.variant.Variant).assertFalse(); canDBus!(std.variant.Variant).assertFalse();
typeSig!(std.variant.Algebraic!(int, double, string)).assertEqual("v"); typeSig!(std.variant.Algebraic!(int, double, string)).assertEqual("v");
@ -251,17 +274,18 @@ unittest {
// ctfe-capable // ctfe-capable
static string sig = typeSig!ulong(); static string sig = typeSig!ulong();
sig.assertEqual("t"); sig.assertEqual("t");
static string sig2 = typeSig!(Tuple!(int,string,string)); static string sig2 = typeSig!(Tuple!(int, string, string));
sig2.assertEqual("(iss)"); sig2.assertEqual("(iss)");
static string sig3 = typeSigAll!(int,string,string); static string sig3 = typeSigAll!(int, string, string);
sig3.assertEqual("iss"); sig3.assertEqual("iss");
} }
private template AllowedFieldTypes(S) if (is(S == struct)) { private template AllowedFieldTypes(S)
if (is(S == struct)) {
import ddbus.attributes : isAllowedField; import ddbus.attributes : isAllowedField;
import std.meta : Filter, staticMap; import std.meta : Filter, staticMap;
static alias TypeOf(alias sym) = typeof(sym); static alias TypeOf(alias sym) = typeof(sym);
alias AllowedFieldTypes = alias AllowedFieldTypes = staticMap!(TypeOf, Filter!(isAllowedField, S.tupleof));
staticMap!(TypeOf, Filter!(isAllowedField, S.tupleof));
} }