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.*
__test__library__
/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`:
```json
"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
`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).
Any forward range can be marshalled in as DBus array of that type but arrays must be taken out as dynamic arrays.
Structures are mapped to `Tuple` from `std.typecons`.
All DBus-compatible basic types work (except file descriptors).
Any forward range can be marshaled in as DBus array of that type but arrays must be taken out as dynamic arrays.
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:
```d
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.signature().assertEqual("ibsai(diaasab)");
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
- `thin`: thin wrapper types
- `router`: message and signal routing based on `MessagePattern` structs.
- `attributes`: defines some UDAs (and related templates) that can be used to customize
struct marshaling.
- `bus`: bus functionality like requesting names and event loops.
- `simple`: simpler wrappers around other functionality.
- `conv`: low level type marshaling methods.
- `util`: templates for working with D type marshaling like `canDBus!T`.
- `exception`: exception classes
- `c_lib`: a D translation of the DBus C headers.
- `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.
These provide most of the functionality you probably want,
Importing `ddbus` will publicly import the `thin`, `router`, `bus`, `simple` and
`attributes` modules. These provide most of the functionality you probably want,
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
contained in the thin wrapper structs to do it yourself.
Nothing is hidden so if `ddbus` doesn't provide something, you can always import
`c_lib` and use the pointers contained in the thin wrapper structs to do it yourself.
# 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 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) {
auto router = new MessageRouter();
MessagePattern patt = MessagePattern("/root","ca.thume.test","test");

View file

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

View file

@ -10,7 +10,9 @@ enum BusPath = "/org/freedesktop/DBus";
enum BusInterface = "org.freedesktop.DBus";
enum NameFlags {
AllowReplace = 1, ReplaceExisting = 2, NoQueue = 4
AllowReplace = 1,
ReplaceExisting = 2,
NoQueue = 4
}
/// Requests a DBus well-known name.
@ -18,8 +20,8 @@ enum NameFlags {
/// Involves blocking call on a DBus method, may throw an exception on failure.
bool requestName(Connection conn, string name,
NameFlags flags = NameFlags.NoQueue | NameFlags.AllowReplace) {
auto msg = Message(BusService,BusPath,BusInterface,"RequestName");
msg.build(name,cast(uint)(flags));
auto msg = Message(BusService, BusPath, BusInterface, "RequestName");
msg.build(name, cast(uint)(flags));
auto res = conn.sendWithReplyBlocking(msg).to!uint;
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.
/// Use only for apps that only do DBus triggered things.
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
@ -39,7 +42,7 @@ bool tick(Connection conn) {
unittest {
import dunit.toolkit;
Connection conn = connectToBus();
conn.requestName("ca.thume.ddbus.testing").assertTrue();
}

View file

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

View file

@ -7,36 +7,37 @@ import ddbus.util;
import ddbus.thin;
import std.exception : enforce;
import std.meta: allSatisfy;
import std.meta : allSatisfy;
import std.string;
import std.typecons;
import std.range;
import std.traits;
import std.variant : VariantN;
void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
foreach(index, arg; args) {
void buildIter(TS...)(DBusMessageIter* iter, TS args)
if (allCanDBus!TS) {
foreach (index, arg; args) {
alias TS[index] T;
static if(is(T == string)) {
static if (is(T == string)) {
immutable(char)* cStr = arg.toStringz();
dbus_message_iter_append_basic(iter,typeCode!T,&cStr);
} else static if(is(T == ObjectPath)) {
dbus_message_iter_append_basic(iter, typeCode!T, &cStr);
} else static if (is(T == ObjectPath)) {
immutable(char)* cStr = arg.toString().toStringz();
dbus_message_iter_append_basic(iter,typeCode!T,&cStr);
} else static if(is(T==bool)) {
dbus_message_iter_append_basic(iter, typeCode!T, &cStr);
} else static if (is(T == bool)) {
dbus_bool_t longerBool = arg; // dbus bools are ints
dbus_message_iter_append_basic(iter,typeCode!T,&longerBool);
} else static if(isTuple!T) {
dbus_message_iter_append_basic(iter, typeCode!T, &longerBool);
} else static if (isTuple!T) {
DBusMessageIter sub;
dbus_message_iter_open_container(iter, 'r', null, &sub);
buildIter(&sub, arg.expand);
dbus_message_iter_close_container(iter, &sub);
} else static if(isInputRange!T) {
} else static if (isInputRange!T) {
DBusMessageIter sub;
const(char)* subSig = (typeSig!(ElementType!T)()).toStringz();
dbus_message_iter_open_container(iter, 'a', subSig, &sub);
foreach(x; arg) {
static if(isInstanceOf!(DictionaryEntry, typeof(x))) {
foreach (x; arg) {
static if (isInstanceOf!(DictionaryEntry, typeof(x))) {
DBusMessageIter entry;
dbus_message_iter_open_container(&sub, 'e', null, &entry);
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);
} else static if(isAssociativeArray!T) {
} else static if (isAssociativeArray!T) {
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);
foreach(k, v; arg) {
foreach (k, v; arg) {
DBusMessageIter entry;
dbus_message_iter_open_container(&sub, 'e', null, &entry);
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(iter, &sub);
} else static if(isInstanceOf!(VariantN, T)) {
enforce(arg.hasValue,
new InvalidValueException(arg, "dbus:" ~ cast(char) typeCode!T));
} else static if (isInstanceOf!(VariantN, T)) {
enforce(arg.hasValue, new InvalidValueException(arg, "dbus:" ~ cast(char) typeCode!T));
DBusMessageIter sub;
foreach(AT; T.AllowedTypes) {
foreach (AT; T.AllowedTypes) {
if (arg.peek!AT) {
dbus_message_iter_open_container(iter, 'v', typeSig!AT.ptr, &sub);
buildIter(&sub, arg.get!AT);
@ -72,8 +72,8 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
break;
}
}
} else static if(is(T == DBusAny) || is(T == Variant!DBusAny)) {
static if(is(T == Variant!DBusAny)) {
} else static if (is(T == DBusAny) || is(T == Variant!DBusAny)) {
static if (is(T == Variant!DBusAny)) {
auto val = arg.data;
val.explicitVariant = true;
} else {
@ -81,66 +81,81 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
}
DBusMessageIter subStore;
DBusMessageIter* sub = &subStore;
const(char)[] sig = [ cast(char) val.type ];
if(val.type == 'a')
const(char)[] sig = [cast(char) val.type];
if (val.type == 'a') {
sig ~= val.signature;
else if(val.type == 'r')
} else if (val.type == 'r') {
sig = val.signature;
}
sig ~= '\0';
if (!val.explicitVariant)
if (!val.explicitVariant) {
sub = iter;
else
} else {
dbus_message_iter_open_container(iter, 'v', sig.ptr, sub);
if(val.type == 's') {
}
if (val.type == 's') {
buildIter(sub, val.str);
} else if(val.type == 'o') {
} else if (val.type == 'o') {
buildIter(sub, val.obj);
} else if(val.type == 'b') {
buildIter(sub,val.boolean);
} else if(dbus_type_is_basic(val.type)) {
dbus_message_iter_append_basic(sub,val.type,&val.int64);
} else if(val.type == 'a') {
} else if (val.type == 'b') {
buildIter(sub, val.boolean);
} else if (dbus_type_is_basic(val.type)) {
dbus_message_iter_append_basic(sub, val.type, &val.int64);
} else if (val.type == 'a') {
DBusMessageIter 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);
else
foreach(item; val.array)
}
} else {
foreach (item; val.array) {
buildIter(&arr, item);
}
}
dbus_message_iter_close_container(sub, &arr);
} else if(val.type == 'r') {
} else if (val.type == 'r') {
DBusMessageIter arr;
dbus_message_iter_open_container(sub, 'r', null, &arr);
foreach(item; val.tuple)
foreach (item; val.tuple) {
buildIter(&arr, item);
}
dbus_message_iter_close_container(sub, &arr);
} else if(val.type == 'e') {
} else if (val.type == 'e') {
DBusMessageIter entry;
dbus_message_iter_open_container(sub, 'e', null, &entry);
buildIter(&entry, val.entry.key);
buildIter(&entry, val.entry.value);
dbus_message_iter_close_container(sub, &entry);
}
if(val.explicitVariant)
if (val.explicitVariant) {
dbus_message_iter_close_container(iter, sub);
} else static if(isInstanceOf!(Variant, T)) {
}
} else static if (isInstanceOf!(Variant, T)) {
DBusMessageIter sub;
const(char)* subSig = typeSig!(VariantType!T).toStringz();
dbus_message_iter_open_container(iter, 'v', subSig, &sub);
buildIter(&sub, arg.data);
dbus_message_iter_close_container(iter, &sub);
} else static if(is(T == struct)) {
} else static if (is(T == struct)) {
DBusMessageIter sub;
dbus_message_iter_open_container(iter, 'r', null, &sub);
// Following failed because of missing 'this' for members of arg.
// That sucks. It worked without Filter.
// 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
foreach(i, member; arg.tupleof) {
foreach (i, member; arg.tupleof) {
// Ugly, but we need to use tupleof again in the condition, because when
// we use `member`, isAllowedField will fail because it'll find this
// 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);
} else static if(basicDBus!T) {
dbus_message_iter_append_basic(iter,typeCode!T,&arg);
} else static if (basicDBus!T) {
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;
alias OriginalType!T B;
B value = readIter!B(iter);
enforce(
only(EnumMembers!T).canFind(value),
new InvalidValueException(value, T.stringof)
);
enforce(only(EnumMembers!T).canFind(value), new InvalidValueException(value, T.stringof));
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;
alias TemplateArgsOf!T[0] E;
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);
enforce(
!(value & ~mask),
new InvalidValueException(value, T.stringof)
);
enforce(!(value & ~mask), new InvalidValueException(value, T.stringof));
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);
T ret;
static if(!isInstanceOf!(Variant, T) || is(T == Variant!DBusAny)) {
if(argType == 'v') {
static if (!isInstanceOf!(Variant, T) || is(T == Variant!DBusAny)) {
if (argType == 'v') {
DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub);
static if(is(T == Variant!DBusAny)) {
static if (is(T == Variant!DBusAny)) {
ret = variant(readIter!DBusAny(&sub));
} else {
ret = readIter!T(&sub);
static if(is(T == DBusAny))
static if (is(T == DBusAny))
ret.explicitVariant = true;
}
dbus_message_iter_next(iter);
@ -206,36 +218,35 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
}
}
static if(
!is(T == DBusAny)
&& !is(T == Variant!DBusAny)
&& !isInstanceOf!(VariantN, T)
) {
enforce(argType == typeCode!T(),
new TypeMismatchException(typeCode!T(), argType));
static if (!is(T == DBusAny) && !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;
dbus_message_iter_get_basic(iter, &cStr);
string str = cStr.fromStringz().idup; // copy string
static if(is(T==string))
static if (is(T == string)) {
ret = str;
else
} else {
ret = ObjectPath(str);
} else static if(is(T==bool)) {
}
} else static if (is(T == bool)) {
dbus_bool_t longerBool;
dbus_message_iter_get_basic(iter, &longerBool);
ret = cast(bool)longerBool;
} else static if(isTuple!T) {
ret = cast(bool) longerBool;
} else static if (isTuple!T) {
DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub);
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);
DBusMessageIter 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;
dbus_message_iter_recurse(&sub, &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);
}
}
} else static if(isInstanceOf!(Variant, T)) {
} else static if (isInstanceOf!(Variant, T)) {
DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub);
ret.data = readIter!(VariantType!T)(&sub);
} else static if(isInstanceOf!(VariantN, T)) {
scope const(char)[] argSig =
dbus_message_iter_get_signature(iter).fromStringz();
scope(exit)
} else static if (isInstanceOf!(VariantN, T)) {
scope const(char)[] argSig = dbus_message_iter_get_signature(iter).fromStringz();
scope (exit)
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.
// Otherwise, in case of container types, we might select the wrong one.
// 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.
enforce(ret.hasValue, new TypeMismatchException(typeCode!T, argType));
} else static if(isAssociativeArray!T) {
} else static if (isAssociativeArray!T) {
DBusMessageIter 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;
dbus_message_iter_recurse(&sub, &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;
dbus_message_iter_next(&sub);
}
} else static if(is(T == DBusAny)) {
} else static if (is(T == DBusAny)) {
ret.type = argType;
ret.explicitVariant = false;
if(ret.type == 's') {
if (ret.type == 's') {
ret.str = readIter!string(iter);
return ret;
} else if(ret.type == 'o') {
} else if (ret.type == 'o') {
ret.obj = readIter!ObjectPath(iter);
return ret;
} else if(ret.type == 'b') {
} else if (ret.type == 'b') {
ret.boolean = readIter!bool(iter);
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);
} else if(ret.type == 'a') {
} else if (ret.type == 'a') {
DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub);
auto sig = dbus_message_iter_get_signature(&sub);
ret.signature = sig.fromStringz.dup;
dbus_free(sig);
if (ret.signature == ['y'])
while(dbus_message_iter_get_arg_type(&sub) != 0) {
if (ret.signature == ['y']) {
while (dbus_message_iter_get_arg_type(&sub) != 0) {
ubyte b;
assert(dbus_message_iter_get_arg_type(&sub) == 'y');
dbus_message_iter_get_basic(&sub, &b);
dbus_message_iter_next(&sub);
ret.binaryData ~= b;
}
else
while(dbus_message_iter_get_arg_type(&sub) != 0) {
} else {
while (dbus_message_iter_get_arg_type(&sub) != 0) {
ret.array ~= readIter!DBusAny(&sub);
}
} else if(ret.type == 'r') {
}
} else if (ret.type == 'r') {
auto sig = dbus_message_iter_get_signature(iter);
ret.signature = sig.fromStringz.dup;
dbus_free(sig);
DBusMessageIter 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);
}
} else if(ret.type == 'e') {
} else if (ret.type == 'e') {
DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub);
ret.entry = new DictionaryEntry!(DBusAny, DBusAny);
ret.entry.key = readIter!DBusAny(&sub);
ret.entry.value = readIter!DBusAny(&sub);
}
} else static if(is(T == struct)) {
} else static if (is(T == struct)) {
DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub);
readIterStruct!T(&sub, ret);
} else static if(basicDBus!T) {
} else static if (basicDBus!T) {
dbus_message_iter_get_basic(iter, &ret);
}
@ -338,15 +354,16 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
return ret;
}
void readIterTuple(Tup)(DBusMessageIter *iter, ref Tup tuple) if(isTuple!Tup && allCanDBus!(Tup.Types)) {
foreach(index, T; Tup.Types) {
void readIterTuple(Tup)(DBusMessageIter* iter, ref Tup tuple)
if (isTuple!Tup && allCanDBus!(Tup.Types)) {
foreach (index, T; Tup.Types) {
tuple[index] = readIter!T(iter);
}
}
void readIterStruct(S)(DBusMessageIter *iter, ref S s) if(is(S == struct) && canDBus!S)
{
foreach(index, T; Fields!S) {
void readIterStruct(S)(DBusMessageIter* iter, ref S s)
if (is(S == struct) && canDBus!S) {
foreach (index, T; Fields!S) {
static if (isAllowedField!(s.tupleof[index])) {
s.tupleof[index] = readIter!T(iter);
}
@ -356,8 +373,12 @@ void readIterStruct(S)(DBusMessageIter *iter, ref S s) if(is(S == struct) && can
unittest {
import dunit.toolkit;
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;
string[string] map;
map["hello"] = "world";
@ -365,24 +386,24 @@ unittest {
anyVar.type.assertEqual('t');
anyVar.uint64.assertEqual(1561);
anyVar.explicitVariant.assertEqual(false);
auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32, [1, 2], tuple(variant(4), 5), map));
Variant!DBusAny complexVar = variant(DBusAny([
"hello world": variant(DBusAny(1337)),
"array value": variant(DBusAny([42, 64])),
"tuple value": variant(tupleMember),
"optimized binary data": variant(DBusAny(cast(ubyte[]) [1, 2, 3, 4, 5, 6]))
]));
auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32,
[1, 2], tuple(variant(4), 5), map));
Variant!DBusAny complexVar = variant(DBusAny(["hello world" : variant(DBusAny(1337)),
"array value" : variant(DBusAny([42, 64])), "tuple value"
: variant(tupleMember), "optimized binary data"
: variant(DBusAny(cast(ubyte[])[1, 2, 3, 4, 5, 6]))]));
complexVar.data.type.assertEqual('a');
complexVar.data.signature.assertEqual("{sv}".dup);
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.signature().assertEqual("ibsvai(diaasabv)a{ss}tv");
msg.read!string().assertThrow!TypeMismatchException();
msg.readTuple!(Tuple!(int, bool, double)).assertThrow!TypeMismatchException();
msg.readTuple!(Tuple!(int, bool, string, double)).assertEqual(tuple(5,true,"wow", 5.9));
msg.readTuple!(Tuple!(int, bool, string, double)).assertEqual(tuple(5, true, "wow", 5.9));
msg.readTuple!(typeof(args))().assertEqual(args);
DBusMessageIter iter;
@ -391,13 +412,14 @@ unittest {
readIter!bool(&iter).assertEqual(true);
readIter!string(&iter).assertEqual("wow");
readIter!double(&iter).assertEqual(5.9);
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!(int[])(&iter).assertEqual([6, 5]);
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.
auto iter2 = iter;
readIter!(string[string])(&iter).assertEqual(["hello": "world"]);
auto dict = readIter!(DictionaryEntry!(string,string)[])(&iter2);
readIter!(string[string])(&iter).assertEqual(["hello" : "world"]);
auto dict = readIter!(DictionaryEntry!(string, string)[])(&iter2);
dict.length.assertEqual(1);
dict[0].key.assertEqual("hello");
dict[0].value.assertEqual("world");
@ -412,8 +434,18 @@ unittest {
import std.variant : Algebraic;
enum E : int { a, b, c }
enum F : uint { x = 1, y = 2, z = 4 }
enum E : int {
a,
b,
c
}
enum F : uint {
x = 1,
y = 2,
z = 4
}
alias V = Algebraic!(byte, short, int, long, string);
Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth2");
@ -437,4 +469,3 @@ unittest {
readIter!V(&iter).assertEqual(v1);
readIter!short(&iter).assertEqual(v2.get!short);
}

View file

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

View file

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

View file

@ -36,19 +36,20 @@ struct MessagePattern {
hash += stringHash(&path);
hash += stringHash(&iface);
hash += stringHash(&method);
hash += (signal?1:0);
hash += (signal ? 1 : 0);
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);
}
}
unittest {
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.signal.assertFalse();
patt.path.assertEqual("/test");
@ -66,30 +67,34 @@ class MessageRouter {
bool handle(Message msg, Connection conn) {
MessageType type = msg.type();
if(type != MessageType.Call && type != MessageType.Signal)
if (type != MessageType.Call && type != MessageType.Signal) {
return false;
}
auto pattern = MessagePattern(msg);
// import std.stdio; debug writeln("Handling ", pattern);
if(pattern.iface == "org.freedesktop.DBus.Introspectable" &&
pattern.method == "Introspect" && !pattern.signal) {
if (pattern.iface == "org.freedesktop.DBus.Introspectable"
&& pattern.method == "Introspect" && !pattern.signal) {
handleIntrospect(pattern.path, msg, conn);
return true;
}
MessageHandler* handler = (pattern in callTable);
if(handler is null) return false;
if (handler is null) {
return false;
}
// Check for matching argument types
version(DDBusNoChecking) {
version (DDBusNoChecking) {
} else {
if(!equal(join(handler.argSig), msg.signature())) {
if (!equal(join(handler.argSig), msg.signature())) {
return false;
}
}
handler.func(msg,conn);
handler.func(msg, conn);
return true;
}
@ -97,25 +102,39 @@ class MessageRouter {
void handlerWrapper(Message call, Connection conn) {
Tuple!Args args = call.readTuple!(Tuple!Args)();
auto retMsg = call.createReturn();
static if(!is(Ret == void)) {
static if (!is(Ret == void)) {
Ret ret = handler(args.expand);
static if (is(Ret == Tuple!T, T...))
static if (is(Ret == Tuple!T, T...)) {
retMsg.build!T(ret.expand);
else
} else {
retMsg.build(ret);
}
} else {
handler(args.expand);
}
if(!patt.signal)
if (!patt.signal) {
conn.send(retMsg);
}
}
static string[] args = typeSigArr!Args;
static if(is(Ret==void)) {
static if (is(Ret == void)) {
static string[] ret = [];
} else {
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;
}
@ -123,38 +142,50 @@ class MessageRouter {
<node name="%s">`;
string introspectXML(string path) {
auto methods = callTable.byKey().filter!(a => (a.path == path) && !a.signal)().array()
// .schwartzSort!((a) => a.iface, "a<b")();
.sort!((a,b) => a.iface < b.iface)();
// dfmt off
auto methods = callTable
.byKey()
.filter!(a => (a.path == path) && !a.signal)
.array
.sort!((a, b) => a.iface < b.iface)();
// dfmt on
auto ifaces = methods.groupBy();
auto app = appender!string;
formattedWrite(app,introspectHeader,path);
foreach(iface; ifaces) {
formattedWrite(app,`<interface name="%s">`,iface.front.iface);
foreach(methodPatt; iface.array()) {
formattedWrite(app,`<method name="%s">`,methodPatt.method);
formattedWrite(app, introspectHeader, path);
foreach (iface; ifaces) {
formattedWrite(app, `<interface name="%s">`, iface.front.iface);
foreach (methodPatt; iface.array()) {
formattedWrite(app, `<method name="%s">`, methodPatt.method);
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("</interface>");
}
string childPath = path;
if(!childPath.endsWith("/")) {
if (!childPath.endsWith("/")) {
childPath ~= "/";
}
auto children = callTable.byKey().filter!(a => (a.path.startsWith(childPath)) && !a.signal)()
.map!((s) => s.path.chompPrefix(childPath))
.map!((s) => s.splitter('/').front)
.array().sort().uniq();
foreach(child; children) {
formattedWrite(app,`<node name="%s"/>`,child);
auto children = callTable.byKey()
.filter!(a => (a.path.startsWith(childPath)) && !a.signal)().map!(
(s) => s.path.chompPrefix(childPath)).map!((s) => s.splitter('/')
.front).array().sort().uniq();
foreach (child; children) {
formattedWrite(app, `<node name="%s"/>`, child);
}
app.put("</node>");
@ -168,31 +199,33 @@ class MessageRouter {
}
}
extern(C) private DBusHandlerResult filterFunc(DBusConnection *dConn, DBusMessage *dMsg, void *routerP) {
MessageRouter router = cast(MessageRouter)routerP;
extern (C) private DBusHandlerResult filterFunc(DBusConnection* dConn,
DBusMessage* dMsg, void* routerP) {
MessageRouter router = cast(MessageRouter) routerP;
dbus_message_ref(dMsg);
Message msg = Message(dMsg);
dbus_connection_ref(dConn);
Connection conn = Connection(dConn);
bool handled = router.handle(msg, conn);
if(handled) {
if (handled) {
return DBusHandlerResult.DBUS_HANDLER_RESULT_HANDLED;
} else {
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);
}
void registerRouter(Connection conn, MessageRouter router) {
void *routerP = cast(void*)router;
void* routerP = cast(void*) router;
GC.addRoot(routerP);
dbus_connection_add_filter(conn.conn, &filterFunc, routerP, &unrootUserData);
}
unittest{
unittest {
import dunit.toolkit;
import std.typecons : BitFlags;
@ -200,31 +233,53 @@ unittest{
auto router = new MessageRouter();
// set up test messages
MessagePattern patt = MessagePattern("/root","ca.thume.test","test");
router.setHandler!(int,int)(patt,(int p) {return 6;});
patt = MessagePattern("/root","ca.thume.tester","lolwut");
router.setHandler!(void,int,string)(patt,(int p, string p2) {});
patt = MessagePattern("/root/wat","ca.thume.tester","lolwut");
router.setHandler!(int,int)(patt,(int p) {return 6;});
patt = MessagePattern("/root/bar","ca.thume.tester","lolwut");
router.setHandler!(Variant!DBusAny,int)(patt,(int p) {return variant(DBusAny(p));});
patt = MessagePattern("/root/foo","ca.thume.tester","lolwut");
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;});
MessagePattern patt = MessagePattern("/root", "ca.thume.test", "test");
router.setHandler!(int, int)(patt, (int p) { return 6; });
patt = MessagePattern("/root", "ca.thume.tester", "lolwut");
router.setHandler!(void, int, string)(patt, (int p, string p2) { });
patt = MessagePattern("/root/wat", "ca.thume.tester", "lolwut");
router.setHandler!(int, int)(patt, (int p) { return 6; });
patt = MessagePattern("/root/bar", "ca.thume.tester", "lolwut");
router.setHandler!(Variant!DBusAny, int)(patt, (int p) {
return variant(DBusAny(p));
});
patt = MessagePattern("/root/foo", "ca.thume.tester", "lolwut");
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) {
if (v.type is typeid(ushort) || v.type is typeid(BitFlags!F)) {
return v.coerce!int;
} else if (v.type is typeid(S)) {
auto s = v.get!S;
final switch (s.f) {
case F.a: return s.b;
case F.b: return cast(int) s.ul;
case F.c: return cast(int) s.ul + s.b;
case F.a:
return s.b;
case F.b:
return cast(int) s.ul;
case F.c:
return cast(int) s.ul + s.b;
}
}
@ -232,8 +287,8 @@ unittest{
});
static assert(!__traits(compiles, {
patt = MessagePattern("/root/bar","ca.thume.tester","lolwut");
router.setHandler!(void, DBusAny)(patt,(DBusAny wrongUsage){return;});
patt = MessagePattern("/root/bar", "ca.thume.tester", "lolwut");
router.setHandler!(void, DBusAny)(patt, (DBusAny wrongUsage) { return; });
}));
// TODO: these tests rely on nondeterministic hash map ordering
@ -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">
<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("/").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();
}
Ret call(Ret, Args...)(string meth, Args args) if(allCanDBus!Args && canDBus!Ret) {
Message msg = Message(dbus_message_new_method_call(dest,path,iface,meth.toStringz()));
Ret call(Ret, Args...)(string meth, Args args)
if (allCanDBus!Args && canDBus!Ret) {
Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz()));
msg.build(args);
Message ret = conn.sendWithReplyBlocking(msg);
return ret.read!Ret();
}
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);
return conn.sendWithReplyBlocking(msg);
}
@ -40,12 +41,13 @@ class PathIface {
unittest {
import dunit.toolkit;
Connection conn = connectToBus();
PathIface obj = new PathIface(conn, "org.freedesktop.DBus","/org/freedesktop/DBus",
"org.freedesktop.DBus");
PathIface obj = new PathIface(conn, "org.freedesktop.DBus",
"/org/freedesktop/DBus", "org.freedesktop.DBus");
auto names = obj.GetNameOwner("org.freedesktop.DBus").to!string();
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;
@ -63,30 +65,39 @@ enum SignalMethod;
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) {
MessagePattern patt = MessagePattern(path,iface,"",false);
foreach(member; __traits(allMembers, T)) {
MessagePattern patt = MessagePattern(path, iface, "", false);
foreach (member; __traits(allMembers, T)) {
// dfmt off
static if (__traits(compiles, __traits(getOverloads, obj, member))
&& __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.signal = hasUDA!(__traits(getOverloads,obj,member)[0], SignalMethod);
router.setHandler(patt, &__traits(getOverloads,obj,member)[0]);
patt.signal = hasUDA!(__traits(getOverloads, obj, member)[0], SignalMethod);
router.setHandler(patt, &__traits(getOverloads, obj, member)[0]);
}
// dfmt on
}
}
unittest {
import dunit.toolkit;
class Tester {
int lol(int x, string s, string[string] map, Variant!DBusAny any) {return 5;}
void wat() {}
@SignalMethod
void signalRecv() {}
int lol(int x, string s, string[string] map, Variant!DBusAny any) {
return 5;
}
void wat() {
}
@SignalMethod void signalRecv() {
}
}
auto o = new Tester;
auto router = new MessageRouter;
registerMethods(router, "/","ca.thume.test",o);
MessagePattern patt = MessagePattern("/","ca.thume.test","wat");
registerMethods(router, "/", "ca.thume.test", o);
MessagePattern patt = MessagePattern("/", "ca.thume.test", "wat");
router.callTable.assertHasKey(patt);
patt.method = "signalRecv";
patt.signal = true;
@ -95,6 +106,6 @@ unittest {
patt.signal = false;
router.callTable.assertHasKey(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"]);
}

View file

@ -3,12 +3,13 @@ module ddbus.thin;
import core.time : Duration;
import ddbus.attributes : isAllowedField;
import ddbus.c_lib;
import ddbus.conv;
import ddbus.exception : TypeMismatchException;
import ddbus.util;
import std.meta : staticIndexOf;
import std.meta : ApplyRight, Filter, staticIndexOf;
import std.string;
import std.typecons;
import std.exception;
@ -48,24 +49,28 @@ struct ObjectPath {
}
ObjectPath opBinary(string op : "~")(string rhs) const pure @safe {
if (!rhs.startsWith("/"))
if (!rhs.startsWith("/")) {
return opBinary!"~"(ObjectPath("/" ~ rhs));
else
} else {
return opBinary!"~"(ObjectPath(rhs));
}
}
ObjectPath opBinary(string op : "~")(ObjectPath rhs) const pure @safe
in {
assert(ObjectPath.isValid(_value) && ObjectPath.isValid(rhs._value));
} out (v) {
}
out (v) {
assert(ObjectPath.isValid(v._value));
} do {
}
do {
ObjectPath ret;
if (_value == "/")
if (_value == "/") {
ret._value = rhs._value;
else
} else {
ret._value = _value ~ rhs._value;
}
return ret;
}
@ -85,20 +90,21 @@ struct ObjectPath {
static bool isValid(string objPath) pure @nogc nothrow @safe {
import std.ascii : isAlphaNum;
if (!objPath.length)
if (!objPath.length) {
return false;
if (objPath == "/")
}
if (objPath == "/") {
return true;
if (objPath[0] != '/' || objPath[$ - 1] == '/')
}
if (objPath[0] != '/' || objPath[$ - 1] == '/') {
return false;
}
// .representation to avoid unicode exceptions -> @nogc & nothrow
return objPath.representation.splitter('/').drop(1)
.all!(a =>
a.length &&
a.all!(c =>
c.isAlphaNum || c == '_'
)
);
return objPath.representation.splitter('/').drop(1).all!(a => 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.
bool explicitVariant;
union
{
union {
///
byte int8;
///
@ -171,117 +176,163 @@ struct DBusAny {
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.type = type;
this.signature = signature;
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) {
static if(is(T == byte) || is(T == ubyte)) {
static if (is(T == byte) || is(T == ubyte)) {
this(typeCode!byte, null, false);
int8 = cast(byte) value;
} else static if(is(T == short)) {
} else static if (is(T == short)) {
this(typeCode!short, null, false);
int16 = cast(short) value;
} else static if(is(T == ushort)) {
} else static if (is(T == ushort)) {
this(typeCode!ushort, null, false);
uint16 = cast(ushort) value;
} else static if(is(T == int)) {
} else static if (is(T == int)) {
this(typeCode!int, null, false);
int32 = cast(int) value;
} else static if(is(T == uint)) {
} else static if (is(T == uint)) {
this(typeCode!uint, null, false);
uint32 = cast(uint) value;
} else static if(is(T == long)) {
} else static if (is(T == long)) {
this(typeCode!long, null, false);
int64 = cast(long) value;
} else static if(is(T == ulong)) {
} else static if (is(T == ulong)) {
this(typeCode!ulong, null, false);
uint64 = cast(ulong) value;
} else static if(is(T == double)) {
} else static if (is(T == double)) {
this(typeCode!double, null, false);
float64 = cast(double) value;
} else static if(isSomeString!T) {
} else static if (isSomeString!T) {
this(typeCode!string, null, false);
str = value.to!string;
} else static if(is(T == bool)) {
} else static if (is(T == bool)) {
this(typeCode!bool, null, false);
boolean = cast(bool) value;
} else static if(is(T == ObjectPath)) {
} else static if (is(T == ObjectPath)) {
this(typeCode!ObjectPath, null, false);
obj = value;
} else static if(is(T == Variant!R, R)) {
static if(is(R == DBusAny)) {
} else static if (is(T == Variant!R, R)) {
static if (is(R == DBusAny)) {
type = value.data.type;
signature = value.data.signature;
explicitVariant = true;
if(type == 'a' || type == 'r') {
if(signature == ['y'])
if (type == 'a' || type == 'r') {
if (signature == ['y']) {
binaryData = value.data.binaryData;
else
} else {
array = value.data.array;
} else if(type == 's')
}
} else if (type == 's') {
str = value.data.str;
else if(type == 'e')
} else if (type == 'e') {
entry = value.data.entry;
else
} else {
uint64 = value.data.uint64;
}
} else {
this(value.data);
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);
entry = new DictionaryEntry!(DBusAny, DBusAny)();
static if(is(K == DBusAny))
static if (is(K == DBusAny)) {
entry.key = value.key;
else
} else {
entry.key = DBusAny(value.key);
static if(is(V == DBusAny))
}
static if (is(V == DBusAny)) {
entry.value = value.value;
else
} else {
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);
binaryData = cast(ubyte[]) value;
} else static if(isInputRange!T) {
} else static if (isInputRange!T) {
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");
this.signature = .typeSig!(ElementType!T);
this.explicitVariant = false;
foreach(elem; value)
foreach (elem; value) {
array ~= DBusAny(elem);
} else static if(isTuple!T) {
}
} else static if (isTuple!T) {
this.type = 'r';
this.signature = ['('];
this.explicitVariant = false;
foreach(index, R; value.Types) {
foreach (index, R; value.Types) {
auto var = DBusAny(value[index]);
tuple ~= var;
if(var.explicitVariant)
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 ~= ')';
} 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')
if (var.type == 'a' || var.type == 'r')
this.signature ~= var.signature;
}
}
}
this.signature ~= ')';
} else static if(isAssociativeArray!T) {
} else static if (isAssociativeArray!T) {
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 valueStr;
switch(type) {
switch (type) {
case typeCode!byte:
valueStr = int8.to!string;
break;
@ -318,10 +369,12 @@ struct DBusAny {
case 'a':
import std.digest.digest : toHexString;
if(signature == ['y'])
if (signature == ['y']) {
valueStr = "binary(" ~ binaryData.toHexString ~ ')';
else
} else {
valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']';
}
break;
case 'r':
valueStr = '(' ~ tuple.map!(a => a.toString).join(", ") ~ ')';
@ -333,10 +386,9 @@ struct DBusAny {
valueStr = "unknown";
break;
}
return "DBusAny(" ~ cast(char) type
~ ", \"" ~ signature.idup
~ "\", " ~ (explicitVariant ? "explicit" : "implicit")
~ ", " ~ valueStr ~ ")";
return "DBusAny(" ~ cast(char) type ~ ", \"" ~ signature.idup ~ "\", " ~ (explicitVariant
? "explicit" : "implicit") ~ ", " ~ valueStr ~ ")";
}
/++
@ -354,24 +406,21 @@ struct DBusAny {
DBusAny object is not the same as the DBus type used to represent T.
+/
T get(T)() @property const
if(staticIndexOf!(T, BasicTypes) >= 0)
{
enforce(type == typeCode!T,
new TypeMismatchException(
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
~ " a value of DBus type '" ~ typeSig ~ "'.", typeCode!T, type));
if (staticIndexOf!(T, BasicTypes) >= 0) {
enforce(type == typeCode!T, new TypeMismatchException(
"Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.",
typeCode!T, type));
static if(isIntegral!T) {
enum memberName =
(isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
static if (isIntegral!T) {
enum memberName = (isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
return __traits(getMember, this, memberName);
} else static if(is(T == double)) {
} else static if (is(T == double)) {
return float64;
} else static if(is(T == string)) {
} else static if (is(T == string)) {
return str;
} else static if(is(T == ObjectPath)) {
} else static if (is(T == ObjectPath)) {
return obj;
} else static if(is(T == bool)) {
} else static if (is(T == bool)) {
return boolean;
} else {
static assert(false);
@ -380,38 +429,34 @@ struct DBusAny {
/// ditto
T get(T)() @property const
if(is(T == const(DBusAny)[]))
{
enforce((type == 'a' && signature != "y") || type == 'r',
new TypeMismatchException(
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
~ " a value of DBus type '" ~ this.typeSig ~ "'.",
typeCode!T, type));
if (is(T == const(DBusAny)[])) {
enforce((type == 'a' && signature != "y") || type == 'r', new TypeMismatchException(
"Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.",
'a', type));
return array;
}
/// ditto
T get(T)() @property const
if (is(T == const(ubyte)[]))
{
enforce(type == 'a' && signature == "y",
new TypeMismatchException(
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
~ " a value of DBus type '" ~ this.typeSig ~ "'.",
typeCode!T, type));
if (is(T == const(ubyte)[])) {
enforce(type == 'a' && signature == "y", new TypeMismatchException(
"Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.",
'a', type));
return binaryData;
}
/// 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] == '{');
DBusAny[DBusAny] aa;
foreach(val; array) {
foreach (val; array) {
enforce(val.type == 'e');
aa[val.entry.key] = val.entry.value;
}
return aa;
}
@ -421,126 +466,162 @@ struct DBusAny {
Returns:
The type signature of the value stored in this DBusAny object.
+/
string typeSig() @property const pure nothrow @safe
{
if(type == 'a') {
string typeSig() @property const pure nothrow @safe {
if (type == 'a') {
return "a" ~ signature;
} else if(type == 'r') {
} else if (type == 'r') {
return signature;
} else if(type == 'e') {
return () @trusted {
} else if (type == 'e') {
return () @trusted{
return "{" ~ entry.key.signature ~ entry.value.signature ~ "}";
} ();
}();
} 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)() {
static if(is(T == Variant!R, R)) {
static if(is(R == DBusAny)) {
/++
Converts a basic type, a tuple or an array to the D type with type checking.
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;
v.explicitVariant = false;
return Variant!R(v);
} else
} else {
return Variant!R(to!R);
} else static if(is(T == DBusAny)) {
}
} else static if (is(T == DBusAny)) {
return this;
} else static if(isIntegral!T || isFloatingPoint!T) {
switch(type) {
case typeCode!byte:
return cast(T) int8;
case typeCode!short:
return cast(T) int16;
case typeCode!ushort:
return cast(T) uint16;
case typeCode!int:
return cast(T) int32;
case typeCode!uint:
return cast(T) uint32;
case typeCode!long:
return cast(T) int64;
case typeCode!ulong:
return cast(T) uint64;
case typeCode!double:
return cast(T) float64;
default:
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
} else {
// In here are all static if blocks that may fall through to the throw
// statement at the bottom of this block.
static if (is(T == DictionaryEntry!(K, V), K, V)) {
if (type == 'e') {
static if (is(T == typeof(entry))) {
return entry;
} else {
return DictionaryEntry(entry.key.to!K, entry.value.to!V);
}
} else static if(is(T == bool)) {
if(type == 'b')
return boolean;
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);
}
} else static if (isAssociativeArray!T) {
if (type == 'a' && (!array.length || array[0].type == 'e')) {
alias K = Unqual!(KeyType!T);
alias V = Unqual!(ValueType!T);
V[K] ret;
foreach (pair; array) {
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 assert(false, "Can't convert variant to " ~ T.stringof);
}
} 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;
}
}
throw new ConvException("Cannot convert from DBus type '" ~ this.typeSig ~ "' to "
~ T.stringof);
}
}
bool opEquals(ref in DBusAny b) const {
if(b.type != type || b.explicitVariant != explicitVariant)
if (b.type != type || b.explicitVariant != explicitVariant) {
return false;
if((type == 'a' || type == 'r') && b.signature != signature)
}
if ((type == 'a' || type == 'r') && b.signature != signature) {
return false;
if(type == 'a' && signature == ['y'])
}
if (type == 'a' && signature == ['y']) {
return binaryData == b.binaryData;
if(type == 'a')
}
if (type == 'a') {
return array == b.array;
else if(type == 'r')
} else if (type == 'r') {
return tuple == b.tuple;
else if(type == 's')
} else if (type == 's') {
return str == b.str;
else if(type == 'o')
} else if (type == 'o') {
return obj == b.obj;
else if(type == 'e')
} else if (type == 'e') {
return entry == b.entry || (entry && b.entry && *entry == *b.entry);
else
} else {
return uint64 == b.uint64;
}
}
}
unittest {
import dunit.toolkit;
DBusAny set(string member, T)(DBusAny v, T value) {
mixin("v." ~ member ~ " = value;");
return v;
@ -548,15 +629,6 @@ unittest {
void test(T)(T value, DBusAny 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);
b.toString();
}
@ -568,8 +640,12 @@ unittest {
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(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(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(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(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(1.84), set!"float64"(DBusAny('d', null, true), 1.84));
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([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", variant(4), variant([1, 2])), set!"tuple"(DBusAny('r', "(svv)", false), [DBusAny("a"), DBusAny(variant(4)), DBusAny(variant([1, 2]))]));
test(tuple("a", 4, [1, 2]), set!"tuple"(DBusAny('r', "(siai)".dup, false),
[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([variant("a"): 4], set!"array"(DBusAny('a', "{vi}", false), [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny(variant("a")), DBusAny(4)))]));
test(["a" : "b"], set!"array"(DBusAny('a', "{ss}", false),
[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
@ -605,17 +691,21 @@ Variant!T variant(T)(T data) {
enum MessageType {
Invalid = 0,
Call, Return, Error, Signal
Call,
Return,
Error,
Signal
}
struct Message {
DBusMessage *msg;
DBusMessage* msg;
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;
}
@ -627,7 +717,8 @@ struct Message {
dbus_message_unref(msg);
}
void build(TS...)(TS args) if(allCanDBus!TS) {
void build(TS...)(TS args)
if (allCanDBus!TS) {
DBusMessageIter iter;
dbus_message_iter_init_append(msg, &iter);
buildIter(&iter, args);
@ -639,14 +730,17 @@ struct Message {
read the first argument. This is suitable for single item returns.
To read multiple arguments use readTuple.
*/
T read(T)() if(canDBus!T) {
T read(T)()
if (canDBus!T) {
DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
return readIter!T(&iter);
}
alias read to;
Tup readTuple(Tup)() if(isTuple!Tup && allCanDBus!(Tup.Types)) {
Tup readTuple(Tup)()
if (isTuple!Tup && allCanDBus!(Tup.Types)) {
DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
Tup ret;
@ -659,7 +753,7 @@ struct Message {
}
MessageType type() {
return cast(MessageType)dbus_message_get_type(msg);
return cast(MessageType) dbus_message_get_type(msg);
}
bool isCall() {
@ -673,21 +767,25 @@ struct Message {
assert(cStr != null);
return cStr.fromStringz().idup;
}
string path() {
const(char)* cStr = dbus_message_get_path(msg);
assert(cStr != null);
return cStr.fromStringz().idup;
}
string iface() {
const(char)* cStr = dbus_message_get_interface(msg);
assert(cStr != null);
return cStr.fromStringz().idup;
}
string member() {
const(char)* cStr = dbus_message_get_member(msg);
assert(cStr != null);
return cStr.fromStringz().idup;
}
string sender() {
const(char)* cStr = dbus_message_get_sender(msg);
assert(cStr != null);
@ -697,13 +795,14 @@ struct Message {
unittest {
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");
}
struct Connection {
DBusConnection *conn;
this(DBusConnection *connection) {
DBusConnection* conn;
this(DBusConnection* connection) {
conn = connection;
}
@ -720,7 +819,7 @@ struct Connection {
}
void send(Message msg) {
dbus_connection_send(conn,msg.msg, null);
dbus_connection_send(conn, msg.msg, null);
}
void sendBlocking(Message msg) {
@ -729,10 +828,10 @@ struct Connection {
}
Message sendWithReplyBlocking(Message msg, int timeout = -1) {
DBusMessage *dbusMsg = msg.msg;
DBusMessage* dbusMsg = msg.msg;
dbus_message_ref(dbusMsg);
DBusMessage *reply = wrapErrors((err) {
auto ret = dbus_connection_send_with_reply_and_block(conn,dbusMsg,timeout,err);
DBusMessage* reply = wrapErrors((err) {
auto ret = dbus_connection_send_with_reply_and_block(conn, dbusMsg, timeout, err);
dbus_message_unref(dbusMsg);
return ret;
});
@ -758,7 +857,7 @@ unittest {
struct S2 {
int h, i;
@(Yes.DBusMarshal) int j, k;
int *p;
int* p;
}
@dbusMarshaling(MarshalingFlag.includePrivateFields)
@ -774,40 +873,34 @@ unittest {
__gshared int dummy;
enum testStruct = S3(
variant(5), "blah",
S1(-7, 63.5, "test"),
S2(84, -123, 78, 432, &dummy),
16
);
msg.build(testStruct);
enum testStruct = S3(variant(5), "blah", S1(-7, 63.5, "test"), S2(84, -123,
78, 432, &dummy), 16);
// Non-marshaled fields should appear as freshly initialized
enum expectedResult = S3(
variant(5), "blah",
S1(int.init, 63.5, "test"),
S2(int.init, int.init, 78, 432, null),
uint.init
);
enum expectedResult = S3(variant(5), "blah", 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);
// Test struct conversion in DBusAny
DBusAny(testStruct).to!S3.assertEqual(expectedResult);
}
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);
}
unittest {
import dunit.toolkit;
// This test will only pass if DBus is installed.
Connection conn = connectToBus();
conn.conn.assertTruthy();
// We can only count on no system bus on OSX
version(OSX) {
version (OSX) {
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.
It seemed best to remove it at that point.
+/
deprecated("Use std.traits.isInstanceOf instead.")
template isVariant(T) {
static if(isBasicType!T || isInputRange!T) {
deprecated("Use std.traits.isInstanceOf instead.") template isVariant(T) {
static if (isBasicType!T || isInputRange!T) {
enum isVariant = false;
} else static if(__traits(compiles, TemplateOf!T)
&& __traits(isSame, TemplateOf!T, Variant)) {
} else static if (__traits(compiles, TemplateOf!T) && __traits(isSame, TemplateOf!T, Variant)) {
enum isVariant = true;
} else {
enum isVariant = false;
@ -45,10 +43,10 @@ template VariantType(T) {
template allCanDBus(TS...) {
static if (TS.length == 0) {
enum allCanDBus = true;
} else static if(!canDBus!(TS[0])) {
} else static if (!canDBus!(TS[0])) {
enum allCanDBus = false;
} else {
enum allCanDBus = allCanDBus!(TS[1..$]);
enum allCanDBus = allCanDBus!(TS[1 .. $]);
}
}
@ -56,26 +54,15 @@ template allCanDBus(TS...) {
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
alias BasicTypes = AliasSeq!(
bool,
byte,
short,
ushort,
int,
uint,
long,
ulong,
double,
string,
ObjectPath
);
alias BasicTypes = AliasSeq!(bool, byte, short, ushort, int, uint, long, ulong,
double, string, ObjectPath);
template basicDBus(T) {
static if(staticIndexOf!(T, BasicTypes) >= 0) {
static if (staticIndexOf!(T, BasicTypes) >= 0) {
enum basicDBus = true;
} else static if(is(T B == enum)) {
} else static if (is(T B == enum)) {
enum basicDBus = basicDBus!B;
} else static if(isInstanceOf!(BitFlags, T)) {
} else static if (isInstanceOf!(BitFlags, T)) {
alias TemplateArgsOf!T[0] E;
enum basicDBus = basicDBus!E;
} else {
@ -84,24 +71,24 @@ template basicDBus(T) {
}
template canDBus(T) {
static if(basicDBus!T || is(T == DBusAny)) {
static if (basicDBus!T || is(T == DBusAny)) {
enum canDBus = true;
} else static if(isInstanceOf!(Variant, T)) {
} else static if (isInstanceOf!(Variant, 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.
enum canDBus = (T.AllowedTypes.length > 0) && allCanDBus!(T.AllowedTypes);
} else static if(isTuple!T) {
} else static if (isTuple!T) {
enum canDBus = allCanDBus!(T.Types);
} else static if(isInputRange!T) {
static if(is(ElementType!T == DictionaryEntry!(K, V), K, V)) {
} else static if (isInputRange!T) {
static if (is(ElementType!T == DictionaryEntry!(K, V), K, V)) {
enum canDBus = basicDBus!K && canDBus!V;
} else {
enum canDBus = canDBus!(ElementType!T);
}
} else static if(isAssociativeArray!T) {
} else static if (isAssociativeArray!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);
} else {
enum canDBus = false;
@ -110,60 +97,63 @@ template canDBus(T) {
unittest {
import dunit.toolkit;
(canDBus!int).assertTrue();
(canDBus!(int[])).assertTrue();
(allCanDBus!(int,string,bool)).assertTrue();
(canDBus!(Tuple!(int[],bool,Variant!short))).assertTrue();
(canDBus!(Tuple!(int[],int[string]))).assertTrue();
(allCanDBus!(int, string, bool)).assertTrue();
(canDBus!(Tuple!(int[], bool, Variant!short))).assertTrue();
(canDBus!(Tuple!(int[], int[string]))).assertTrue();
(canDBus!(int[string])).assertTrue();
}
string typeSig(T)() if(canDBus!T) {
static if(is(T == byte)) {
string typeSig(T)()
if (canDBus!T) {
static if (is(T == byte)) {
return "y";
} else static if(is(T == bool)) {
} else static if (is(T == bool)) {
return "b";
} else static if(is(T == short)) {
} else static if (is(T == short)) {
return "n";
} else static if(is(T == ushort)) {
} else static if (is(T == ushort)) {
return "q";
} else static if(is(T == int)) {
} else static if (is(T == int)) {
return "i";
} else static if(is(T == uint)) {
} else static if (is(T == uint)) {
return "u";
} else static if(is(T == long)) {
} else static if (is(T == long)) {
return "x";
} else static if(is(T == ulong)) {
} else static if (is(T == ulong)) {
return "t";
} else static if(is(T == double)) {
} else static if (is(T == double)) {
return "d";
} else static if(is(T == string)) {
} else static if (is(T == string)) {
return "s";
} else static if(is(T == ObjectPath)) {
} else static if (is(T == ObjectPath)) {
return "o";
} else static if(isInstanceOf!(Variant, T) || isInstanceOf!(VariantN, T)) {
} else static if (isInstanceOf!(Variant, T) || isInstanceOf!(VariantN, T)) {
return "v";
} else static if(is(T B == enum)) {
} else static if (is(T B == enum)) {
return typeSig!B;
} else static if(isInstanceOf!(BitFlags, T)) {
} else static if (isInstanceOf!(BitFlags, T)) {
alias TemplateArgsOf!T[0] E;
return typeSig!E;
} else static if(is(T == DBusAny)) {
static assert(false, "Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired.");
} else static if(isTuple!T) {
} else static if (is(T == DBusAny)) {
static assert(false,
"Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired.");
} else static if (isTuple!T) {
string sig = "(";
foreach(i, S; T.Types) {
foreach (i, S; T.Types) {
sig ~= typeSig!S();
}
sig ~= ")";
return sig;
} else static if(isInputRange!T) {
} else static if (isInputRange!T) {
return "a" ~ typeSig!(ElementType!T)();
} else static if(isAssociativeArray!T) {
} else static if (isAssociativeArray!T) {
return "a{" ~ typeSig!(KeyType!T) ~ typeSig!(ValueType!T) ~ "}";
} else static if(is(T == struct)) {
} else static if (is(T == struct)) {
string sig = "(";
foreach(i, S; AllowedFieldTypes!T) {
foreach (i, S; AllowedFieldTypes!T) {
sig ~= typeSig!S();
}
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.value) V;
return "{" ~ typeSig!K ~ typeSig!V ~ '}';
}
string[] typeSigReturn(T)() if(canDBus!T) {
static if(is(T == Tuple!TS, TS...))
string[] typeSigReturn(T)()
if (canDBus!T) {
static if (is(T == Tuple!TS, TS...))
return typeSigArr!TS;
else
return [typeSig!T];
}
string typeSigAll(TS...)() if(allCanDBus!TS) {
string typeSigAll(TS...)()
if (allCanDBus!TS) {
string sig = "";
foreach(i,T; TS) {
foreach (i, T; TS) {
sig ~= typeSig!T();
}
return sig;
}
string[] typeSigArr(TS...)() if(allCanDBus!TS) {
string[] typeSigArr(TS...)()
if (allCanDBus!TS) {
string[] sig = [];
foreach(i,T; TS) {
foreach (i, T; TS) {
sig ~= typeSig!T();
}
return sig;
}
int typeCode(T)() if(canDBus!T) {
int typeCode(T)()
if (canDBus!T) {
int code = typeSig!T()[0];
return (code != '(') ? code : 'r';
}
int typeCode(T)() if(isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) {
int typeCode(T)()
if (isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) {
return 'e';
}
unittest {
import dunit.toolkit;
// basics
typeSig!int().assertEqual("i");
typeSig!bool().assertEqual("b");
typeSig!string().assertEqual("s");
typeSig!(Variant!int)().assertEqual("v");
// enums
enum E : byte { a, b, c }
enum E : byte {
a,
b,
c
}
typeSig!E().assertEqual(typeSig!byte());
enum U : string { One = "One", Two = "Two" }
enum U : string {
One = "One",
Two = "Two"
}
typeSig!U().assertEqual(typeSig!string());
// 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());
// tuples (represented as structs in DBus)
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, string)).assertEqual("(iss)");
typeSig!(Tuple!(int, string, Variant!int, Tuple!(int, "k", double, "x"))).assertEqual(
"(isv(id))");
// structs
struct S1 { int a; double b; string s; }
struct S1 {
int a;
double b;
string s;
}
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)");
// arrays
typeSig!(int[]).assertEqual("ai");
@ -240,7 +263,7 @@ unittest {
typeSig!(int[string]).assertEqual("a{si}");
typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}");
// multiple arguments
typeSigAll!(int,bool).assertEqual("ib");
typeSigAll!(int, bool).assertEqual("ib");
// Phobos-style variants
canDBus!(std.variant.Variant).assertFalse();
typeSig!(std.variant.Algebraic!(int, double, string)).assertEqual("v");
@ -251,17 +274,18 @@ unittest {
// ctfe-capable
static string sig = typeSig!ulong();
sig.assertEqual("t");
static string sig2 = typeSig!(Tuple!(int,string,string));
static string sig2 = typeSig!(Tuple!(int, string, string));
sig2.assertEqual("(iss)");
static string sig3 = typeSigAll!(int,string,string);
static string sig3 = typeSigAll!(int, string, string);
sig3.assertEqual("iss");
}
private template AllowedFieldTypes(S) if (is(S == struct)) {
private template AllowedFieldTypes(S)
if (is(S == struct)) {
import ddbus.attributes : isAllowedField;
import std.meta : Filter, staticMap;
static alias TypeOf(alias sym) = typeof(sym);
alias AllowedFieldTypes =
staticMap!(TypeOf, Filter!(isAllowedField, S.tupleof));
alias AllowedFieldTypes = staticMap!(TypeOf, Filter!(isAllowedField, S.tupleof));
}