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:
parent
99cb6cb071
commit
ce7716463d
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue