diff --git a/luad/conversions/enums.d b/luad/conversions/enums.d
new file mode 100644
index 0000000..974861f
--- /dev/null
+++ b/luad/conversions/enums.d
@@ -0,0 +1,173 @@
+/**
+Internal module for pushing and getting _enums.
+
+Enum's are treated in Lua as strings.
+
+Conversion of enum keys is case-insensitive, which I think is more useful for Lua's typical 'config' style usage
+
+This is still a work-in-progress. Outstanding issues include:
+ Handling of bitfields.
+ Assignment of integer keys?
+ Conversion function needs to be improved (linear search! >_<)
+*/
+module luad.conversions.enums;
+
+import luad.c.all;
+import luad.stack;
+
+import std.traits;
+import std.conv;
+import std.range;
+import std.string;
+
+struct KeyValuePair(ValueType)
+{
+ string key;
+ ValueType value;
+}
+
+// produce a tuple of KeyValuePair's for an enum.
+template EnumKeyValuePair(Enum)
+{
+ template impl(size_t len, size_t offset, Items...)
+ {
+ static if(offset == len)
+ alias impl = TypeTuple!();
+ else
+ alias impl = TypeTuple!(KeyValuePair!Enum(Items[offset], Items[len + offset]), impl!(len, offset + 1, Items));
+ }
+
+ alias Keys = TypeTuple!(__traits(allMembers, Enum));
+ alias Values = EnumMembers!Enum;
+ static assert(Keys.length == Values.length);
+
+ alias EnumKeyValuePair = impl!(Keys.length, 0, TypeTuple!(Keys, Values));
+}
+
+immutable(KeyValuePair!Enum)[] getKeyValuePairs(Enum)() pure nothrow @nogc
+{
+ static immutable(KeyValuePair!Enum[]) kvp = [ EnumKeyValuePair!Enum ];
+ return kvp;
+}
+
+// TODO: These linear lookups are pretty crappy... we can do better, but this get's us working.
+Enum getEnumValue(Enum)(lua_State* L, const(char)[] value) if(is(Enum == enum))
+{
+ value = value.strip;
+ if(!value.empty)
+ {
+ auto kvp = getKeyValuePairs!Enum();
+ foreach(ref i; kvp)
+ {
+ if(!icmp(i.key, value)) // case inseneitive enum keys...
+ return i.value;
+ }
+ }
+ luaL_error(L, "invalid enum key '%s' for enum type %s", value.ptr, Enum.stringof.ptr);
+ return Enum.init;
+}
+
+string getEnumFromValue(Enum)(Enum value)
+{
+ auto kvp = getKeyValuePairs!Enum();
+ foreach(ref i; kvp)
+ {
+ if(value == i.value)
+ return i.key;
+ }
+ return null;
+}
+
+void pushEnum(T)(lua_State* L, T value) if (is(T == enum))
+{
+ string key = getEnumFromValue(value);
+ if(key)
+ lua_pushlstring(L, key.ptr, key.length);
+ else
+ luaL_error(L, "invalid value for enum type %s", T.stringof.ptr);
+}
+
+T getEnum(T)(lua_State* L, int idx) if(is(T == enum))
+{
+ // TODO: check to see if idx is a number, if it is, convert it directly?
+
+ size_t len;
+ const(char)* s = lua_tolstring(L, idx, &len);
+ return getEnumValue!T(L, s[0..len]);
+}
+
+void pushStaticTypeInterface(T)(lua_State* L) if(is(T == enum))
+{
+ lua_newtable(L);
+
+ // TODO: we could get fancy and make an __index table of keys, so that they are read-only
+ // ... but for now, we'll just populate a table with the keys as strings
+
+ // set 'init'
+ string initVal = getEnumFromValue(T.init);
+ lua_pushlstring(L, initVal.ptr, initVal.length);
+ lua_setfield(L, -2, "init");
+
+ // TODO: integral enums also have 'min' and 'max'
+
+ // we'll create tables for the keys and valyes arrays.
+ lua_newtable(L); // keys
+ lua_newtable(L); // values
+
+ // add the enum keys
+ auto kvp = getKeyValuePairs!T();
+ foreach(int i, ref e; kvp)
+ {
+ // set the key to the key string (lua will carry enums by string)
+ lua_pushlstring(L, e.key.ptr, e.key.length);
+ lua_setfield(L, -4, e.key.ptr);
+
+ // push the key to the keys array
+ lua_pushlstring(L, e.key.ptr, e.key.length);
+ lua_rawseti(L, -3, i+1);
+
+ // push the value to the values array
+ pushValue!(OriginalType!T)(L, e.value);
+ lua_rawseti(L, -2, i+1);
+ }
+
+ lua_setfield(L, -3, "values");
+ lua_setfield(L, -2, "keys");
+}
+
+version(unittest)
+{
+ import luad.base;
+
+ enum E
+ {
+ Key0,
+ Key1
+ }
+}
+
+unittest
+{
+ import luad.testing;
+
+ lua_State* L = luaL_newstate();
+ scope(success) lua_close(L);
+ luaL_openlibs(L);
+
+ pushValue(L, E.Key0);
+ assert(lua_isstring(L, -1));
+ lua_setglobal(L, "enum");
+
+ unittest_lua(L, `
+ assert(enum == "Key0")
+
+ enum = "key1"
+ `);
+
+ lua_getglobal(L, "enum");
+ E e = getValue!E(L, -1);
+ assert(e == E.Key1);
+ lua_pop(L, 1);
+
+ // TODO: test the enum type interface...
+}
diff --git a/luad/stack.d b/luad/stack.d
index d1cf6f5..8f0eeb5 100644
--- a/luad/stack.d
+++ b/luad/stack.d
@@ -71,6 +71,7 @@ import luad.conversions.arrays;
import luad.conversions.structs;
import luad.conversions.assocarrays;
import luad.conversions.classes;
+import luad.conversions.enums;
import luad.conversions.variant;
/**
@@ -90,6 +91,9 @@ void pushValue(T)(lua_State* L, T value)
else static if(is(T == Nil))
lua_pushnil(L);
+ else static if(is(T == enum))
+ pushEnum(L, value);
+
else static if(is(T == bool))
lua_pushboolean(L, cast(bool)value);
@@ -157,7 +161,10 @@ template isVoidArray(T)
*/
template luaTypeOf(T)
{
- static if(is(T == bool))
+ static if(is(T == enum))
+ enum luaTypeOf = LUA_TSTRING;
+
+ else static if(is(T == bool))
enum luaTypeOf = LUA_TBOOLEAN;
else static if(is(T == Nil))
@@ -251,6 +258,9 @@ T getValue(T, alias typeMismatchHandler = defaultTypeMismatch)(lua_State* L, int
else static if(is(T == Nil))
return nil;
+ else static if(is(T == enum))
+ return getEnum!T(L, idx);
+
else static if(is(T == bool))
return lua_toboolean(L, idx);
diff --git a/luad/state.d b/luad/state.d
index d58757a..6c350bc 100644
--- a/luad/state.d
+++ b/luad/state.d
@@ -8,6 +8,7 @@ import std.typecons : isTuple;
import luad.c.all;
import luad.stack;
import luad.conversions.classes;
+import luad.conversions.enums;
import luad.base, luad.table, luad.lfunction, luad.dynamic, luad.error;
diff --git a/visuald/LuaD.visualdproj b/visuald/LuaD.visualdproj
index 8218571..ce04125 100644
--- a/visuald/LuaD.visualdproj
+++ b/visuald/LuaD.visualdproj
@@ -296,6 +296,7 @@
+