Add get!T method to DBusAny (#27)

* Add ddbus.util.BasicTypes

* Implement DBusAny.get and DBusAny.typeSig

* Add unittest for DBusAny.get

* Make TypeMismatchException constructor public
This commit is contained in:
thaven 2017-11-11 11:42:55 +01:00 committed by GitHub
parent 99cb6cb071
commit ce7716463d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 153 additions and 19 deletions

View file

@ -36,7 +36,9 @@ class DBusException : Exception {
} }
/++ /++
Thrown when the signature of a message does not match the requested types. Thrown when the signature of a message does not match the requested types or
when trying to get a value from a DBusAny object that does not match the type
of its actual value.
+/ +/
class TypeMismatchException : Exception { class TypeMismatchException : Exception {
package this( package this(
@ -45,16 +47,34 @@ class TypeMismatchException : Exception {
string file = __FILE__, string file = __FILE__,
size_t line = __LINE__, size_t line = __LINE__,
Throwable next = null Throwable next = null
) pure nothrow @safe {
string message;
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 ~ '\'';
} 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 ~ '\'';
}
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 { ) pure nothrow @safe {
_expectedType = expectedType; _expectedType = expectedType;
_actualType = actualType; _actualType = actualType;
if (expectedType == 'v') { super(message, file, line, next);
super("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);
} else {
super("The type of value at the current position in the message does not match the type of value to be read."
~ " Expected: " ~ cast(char) expectedType ~ ", Got: " ~ cast(char) actualType);
}
} }
int expectedType() @property pure const nothrow @safe @nogc { int expectedType() @property pure const nothrow @safe @nogc {

View file

@ -3,7 +3,10 @@ module ddbus.thin;
import ddbus.c_lib; import ddbus.c_lib;
import ddbus.conv; import ddbus.conv;
import ddbus.exception : TypeMismatchException;
import ddbus.util; import ddbus.util;
import std.meta : staticIndexOf;
import std.string; import std.string;
import std.typecons; import std.typecons;
import std.exception; import std.exception;
@ -127,7 +130,7 @@ struct DBusAny {
/// DBus type of the value (never 'v'), see typeSig!T /// DBus type of the value (never 'v'), see typeSig!T
int type; int type;
/// Child signature for Arrays & Tuples /// Child signature for Arrays & Tuples
const(char)[] signature; string signature;
/// If true, this value will get serialized as variant value, otherwise it is serialized like it wasn't in a DBusAny wrapper. /// If true, this value will get serialized as variant value, otherwise it is serialized like it wasn't in a DBusAny wrapper.
/// 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;
@ -159,7 +162,7 @@ struct DBusAny {
/// ///
DBusAny[] array; DBusAny[] array;
/// ///
DBusAny[] tuple; alias tuple = array;
/// ///
DictionaryEntry!(DBusAny, DBusAny)* entry; DictionaryEntry!(DBusAny, DBusAny)* entry;
/// ///
@ -167,7 +170,7 @@ struct DBusAny {
} }
/// Manually creates a DBusAny object using a type, signature and implicit specifier. /// Manually creates a DBusAny object using a type, signature and implicit specifier.
this(int type, const(char)[] 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;
@ -246,8 +249,8 @@ struct DBusAny {
} 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);
@ -319,7 +322,7 @@ struct DBusAny {
valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']'; valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']';
break; break;
case 'r': case 'r':
valueStr = '(' ~ array.map!(a => a.toString).join(", ") ~ ')'; valueStr = '(' ~ tuple.map!(a => a.toString).join(", ") ~ ')';
break; break;
case 'e': case 'e':
valueStr = entry.key.toString ~ ": " ~ entry.value.toString; valueStr = entry.key.toString ~ ": " ~ entry.value.toString;
@ -334,6 +337,71 @@ struct DBusAny {
~ ", " ~ valueStr ~ ")"; ~ ", " ~ valueStr ~ ")";
} }
/++
Get the value stored in the DBusAny object.
Parameters:
T = The requested type. The currently stored value must match the
requested type exactly.
Returns:
The current value of the DBusAny object.
Throws:
TypeMismatchException if the DBus type of the current value of the
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));
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)) {
return float64;
} else static if(is(T == string)) {
return str;
} else static if(is(T == ObjectPath)) {
return obj;
} else static if(is(T == bool)) {
return boolean;
} else {
static assert(false);
}
}
/// 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));
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));
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() { DBusAny[DBusAny] toAA() {
enforce(type == 'a' && signature && signature[0] == '{'); enforce(type == 'a' && signature && signature[0] == '{');
@ -345,6 +413,27 @@ struct DBusAny {
return aa; return aa;
} }
/++
Get the DBus type signature of the value stored in the DBusAny object.
Returns:
The type signature of the value stored in this DBusAny object.
+/
string typeSig() @property const pure nothrow @safe
{
if(type == 'a') {
return "a" ~ signature;
} else if(type == 'r') {
return signature;
} else if(type == 'e') {
return () @trusted {
return "{" ~ entry.key.signature ~ entry.value.signature ~ "}";
} ();
} else {
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. /// 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)() { T to(T)() {
static if(is(T == Variant!R, R)) { static if(is(T == Variant!R, R)) {
@ -457,6 +546,15 @@ 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();
} }

View file

@ -1,9 +1,10 @@
module ddbus.util; module ddbus.util;
import ddbus.thin; import ddbus.thin;
import std.typecons; import std.meta : AliasSeq, staticIndexOf;
import std.range; import std.range;
import std.traits; import std.traits;
import std.typecons : BitFlags, isTuple, Tuple;
import std.variant : VariantN; import std.variant : VariantN;
struct DictionaryEntry(K, V) { struct DictionaryEntry(K, V) {
@ -51,11 +52,26 @@ 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
);
template basicDBus(T) { template basicDBus(T) {
static if(is(T == byte) || is(T == short) || is (T == ushort) || is (T == int) static if(staticIndexOf!(T, BasicTypes) >= 0) {
|| is (T == uint) || is (T == long) || is (T == ulong)
|| is (T == double) || is (T == string) || is(T == bool)
|| is (T == ObjectPath)) {
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;