Skip to content

Instantly share code, notes, and snippets.

@nadako
Last active January 3, 2016 12:38
Show Gist options
  • Save nadako/8463669 to your computer and use it in GitHub Desktop.
Save nadako/8463669 to your computer and use it in GitHub Desktop.
Check command arguments object for missing keys and bad type.
#if macro
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
import haxe.macro.ComplexTypeTools;
class CommandMacro
{
public static function build():Array<Field>
{
var fields = Context.getBuildFields();
for (field in fields)
{
if (field.name == "execute")
{
switch (field.kind)
{
case FFun(f):
if (f.args.length != 1)
Context.error("execute function must take only one argument", field.pos);
var args = f.args[0];
if (args.opt)
Context.error("execute function argument must not be optional", field.pos);
if (args.name == "_") // allow skipping args
continue;
if (args.type == null)
Context.error("execute function argument must be explicitly typed", field.pos);
var struct = resolveTypeDefs(ComplexTypeTools.toType(args.type), field.pos);
var exprs = [];
for (arg in struct.fields)
{
var checkExprs = generateCheckExpr(args.name, arg);
if (checkExprs != null)
{
for (e in checkExprs) exprs.push(e);
}
}
exprs.push(f.expr);
f.expr = {expr: EBlock(exprs), pos: field.pos};
field.kind = FFun(f);
default:
}
}
}
return fields;
}
static function generateCheckExpr(argsName:String, field:ClassField):Array<Expr>
{
var checks = [];
var fieldName = field.name;
var argName = '$argsName.$fieldName';
var hasFieldExpr = macro Reflect.hasField($i{argsName}, $v{fieldName});
var argExpr = macro $p{[argsName, fieldName]};
var optional = field.meta.has(":optional");
// проверяем наличие аргумента, если он обязательный
if (!optional)
{
var error = '$argName: Отсутствует обязательное поле';
checks.push(macro if (!$hasFieldExpr) throw new Error($v{error}));
}
// избавляемся от Null<T>, который нам добавляет optional-ность аргумента
var type = switch (field.type)
{
case TType(td, params):
var td = td.get();
if (td.name == "Null")
params[0];
else
field.type;
default:
field.type;
};
function addCheck(check:Expr, error:String):Void
{
if (optional)
checks.push(macro if ($hasFieldExpr && $check) throw new Error($v{error}));
else
checks.push(macro if ($check) throw new Error($v{error}));
}
// проверяем тип аргумента
switch (type)
{
case TAbstract(t, params):
var t = t.get();
switch (t)
{
case {pack: [], name: "Float"}:
addCheck(macro !Std.is($argExpr, Float), '$argName: Значение должно быть числом');
case {pack: [], name: "Int"}:
addCheck(macro !Std.is($argExpr, Int), '$argName: Значение должно быть целым');
case {pack: [], name: "Bool"}:
addCheck(macro !Std.is($argExpr, Bool), '$argName: Значение должно быть булевым');
}
case TInst(t, params):
var t = t.get();
switch (t)
{
case {pack: [], name: "String"}:
addCheck(macro !Std.is($argExpr, String), '$argName: Значение должно быть строкой');
}
default:
}
// для time и item_id добавляем проверку что оно больше 0
if (fieldName == "time" || fieldName == "item_id")
checks.push(macro if ($argExpr <= 0) throw new Error($v{'$argName: Значение должно быть больше 0'}));
return checks;
}
static function resolveTypeDefs(type:Type, pos:Position):AnonType
{
switch (type)
{
case TAnonymous(ref):
return ref.get();
case TType(ref, _):
return resolveTypeDefs(ref.get().type, pos);
default:
Context.error("execute function argument can only be anonymous struct or a typedef to an anonymous struct", pos);
return null;
}
}
}
#end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment