Last active
January 3, 2016 12:38
-
-
Save nadako/8463669 to your computer and use it in GitHub Desktop.
Check command arguments object for missing keys and bad type.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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