<commit_before>/*
 * Copyright (c) 2005-2008, The haXe Project Contributors
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
package haxe.web;

#if macro
import haxe.macro.Expr;
import haxe.macro.Type.ClassField;
import haxe.macro.Context;
#end

typedef DispatchConfig = {
	var obj : Dynamic;
	var rules : Dynamic;
}

typedef Lock<T> = T;

enum MatchRule {
	MRInt;
	MRBool;
	MRFloat;
	MRString;
	MRDispatch;
	MRSpod( c : String, lock : Bool );
	MROpt( r : MatchRule );
}

enum DispatchRule {
	DRMatch( r : MatchRule );
	DRMult( r : Array<MatchRule> );
	DRArgs( r : DispatchRule, args : Array<{ name : String, rule : MatchRule, opt : Bool }>, opt : Bool );
	DRMeta( r : DispatchRule );
}

enum DispatchError {
	DENotFound( part : String );
	DEInvalidValue;
	DEMissing;
	DEMissingParam( p : String );
}

private class Redirect {
	public function new() {
	}
}

class Dispatch {

	public var parts : Array<String>;
	public var params : Hash<String>;
	public var name : String;
	public var cfg : DispatchConfig;

	public function new(url:String, params) {
		parts = url.split("/");
		if( parts[0] == "" ) parts.shift();
		this.params = params;
	}

	@:macro public function dispatch( ethis : Expr, obj : ExprRequire<{}> ) : ExprRequire<Void> {
		var p = Context.currentPos();
		var cfg = makeConfig(obj);
		return { expr : ECall({ expr : EField(ethis, "runtimeDispatch"), pos : p }, [cfg]), pos : p };
	}

	@:macro public function getParams( ethis : Expr ) : Expr {
		var p = Context.currentPos();
		if( PARAMS == null ) {
			PARAMS = new Array();
			Context.onGenerate(buildParams);
		}
		var index = PARAMS.length;
		var t = Context.typeof({ expr : EConst(CIdent("null")), pos : p });
		PARAMS.push( { p : p, t : t } );
		var call = { expr : ECall( { expr : EField(ethis, "runtimeGetParams"), pos : p }, [ { expr : EConst(CInt(Std.string(index))), pos : p } ]), pos : p };
		var rt = TPath( { pack : ["haxe", "macro"], name : "MacroType", params : [TPExpr(Context.parse("haxe.web.Dispatch.getRunParam("+index+")",p))], sub : null } );
		return { expr : EBlock([ { expr : EVars([ { name : "tmp", type : rt, expr : call } ]), pos : p }, { expr : EConst(CIdent("tmp")), pos : p } ]), pos : p };
	}

	public dynamic function onMeta( v : String, args : Null<Array<Dynamic>> ) {
	}

	function resolveName( name : String ) {
		return name;
	}

	public function runtimeDispatch( cfg : DispatchConfig ) {
		name = parts.shift();
		if( name == null )
			name = "default";
		name = resolveName(name);
		this.cfg = cfg;
		var r : DispatchRule = Reflect.field(cfg.rules, name);
		if( r == null ) {
			r = Reflect.field(cfg.rules, "default");
			if( r == null )
				throw DENotFound(name);
			parts.unshift(name);
			name = "default";
		}
		name = "do" + name.charAt(0).toUpperCase() + name.substr(1);
		var args = [];
		loop(args, r);
		try {
			Reflect.callMethod(cfg.obj, Reflect.field(cfg.obj, name), args);
		} catch( e : Redirect ) {
			runtimeDispatch(cfg);
		}
	}

	public function redirect( url : String, ?params : Hash<String> ) {
		parts = url.split("/");
		if( parts[0] == "" ) parts.shift();
		if( params != null ) this.params = params;
		throw new Redirect();
	}

	static var GET_RULES;
	public function runtimeGetParams( cfgIndex : Int ) : Dynamic {
		if( GET_RULES == null )
			GET_RULES = haxe.Unserializer.run(haxe.rtti.Meta.getType(Dispatch).getParams[0]);
		return checkParams(GET_RULES[cfgIndex], true);
	}

	function match( v : String, r : MatchRule, opt : Bool ) : Dynamic {
		switch( r ) {
		case MRInt:
			if( v == null ) throw DEMissing;
			if( opt && v == "" ) return null;
			var v = Std.parseInt(v);
			if( v == null ) throw DEInvalidValue;
			return v;
		case MRFloat:
			if( v == null ) throw DEMissing;
			if( opt && v == "" ) return null;
			var v = Std.parseFloat(v);
			if( Math.isNaN(v) ) throw DEInvalidValue;
			return v;
		case MRString:
			if( v == null ) throw DEMissing;
			return v;
		case MRBool:
			return v != null && v != "0" && v != "false" && v != "null";
		case MRDispatch:
			if( v != null )
				parts.unshift(v);
			return this;
		case MRSpod(c, lock):
			if( v == null ) throw DEMissing;
			var v = Std.parseInt(v);
			if( v == null ) throw DEInvalidValue;
			var cl : Dynamic = Type.resolveClass(c);
			if( cl == null ) throw "assert";
			var o : Dynamic;
			#if spod_macro
			o = cl.manager.unsafeGet(v, lock);
			#else
			o = cl.manager.get(v, lock);
			#end
			if( o == null ) throw DEInvalidValue;
			return o;
		case MROpt(r) :
			if( v == null )
				return null;
			return match(v, r, true);
		}
	}

	function checkParams( params : Array<{ name : String, opt : Bool, rule : MatchRule }>, opt ) {
		var po = { };
		for( p in params ) {
			var v = this.params.get(p.name);
			if( v == null ) {
				if( p.opt ) continue;
				if( opt ) return null;
				throw DEMissingParam(p.name);
			}
			Reflect.setField(po, p.name, match(v, p.rule,p.opt));
		}
		return po;
	}

	function loop( args : Array<Dynamic>, r ) {
		switch( r ) {
		case DRArgs(r, params, opt):
			loop(args, r);
			args.push( checkParams(params, opt) );
		case DRMatch(r):
			args.push(match(parts.shift(), r, false));
		case DRMult(rl):
			for( r in rl )
				args.push(match(parts.shift(), r, false));
		case DRMeta(r):
			loop(args, r);
			var c = Type.getClass(cfg.obj);
			if( c == null ) throw "assert";
			var m = Reflect.field(haxe.rtti.Meta.getFields(c), name);
			if( m == null ) throw "assert";
			for( mv in Reflect.fields(m) )
				onMeta(mv, Reflect.field(m, mv));
		}
	}

	#if macro

	static inline var PREFIX = "haxe.web.Dispatch.DispatchRule.";

	static function getType(t,p) {
		switch( Context.follow(t) ) {
		case TInst(i,_):
			switch( i.toString() ) {
			case "Int":
				return MRInt;
			case "Float":
				return MRFloat;
			case "String":
				return MRString;
			case "haxe.web.Dispatch":
				return MRDispatch;
			default:
				var c = i.get();
				var csup = c.superClass;
				while( csup != null ) {
					var name = csup.t.toString();
					if( name == "neko.db.Object" || name == "sys.db.Object" ) {
						var lock = switch( t ) {
						case TType(t, _): t.get().name == "Lock";
						default: false;
						}
						return MRSpod(i.toString(), lock);
					}
					csup = csup.t.get().superClass;
				}
				Context.error("Unsupported dispatch type '"+i.toString()+"'",p);
			}
		case TEnum(e, _):
			switch( e.toString() ) {
			case "Bool":
				return MRBool;
			default:
				Context.error("Unsupported dispatch type "+e.toString(),p);
			}
		default:
			Context.error("Unsupported dispatch type "+Std.string(t),p);
		}
		return null;
	}

	static function makeArgs( t : haxe.macro.Type, p ) {
		var args = [];
		switch( Context.follow(t) ) {
		case TAnonymous(a):
			for( f in a.get().fields ) {
				var r = getType(f.type, f.pos);
				var opt = false;
				switch( f.type ) {
				case TType(t, _):
					if( t.get().name == "Null" ) opt = true;
				default:
				}
				if( r == MRBool ) opt = true;
				args.push( { name : f.name, rule : r, opt : opt } );
			}
		default:
			Context.error("Arguments should be an anonymous object", p);
		}
		return args;
	}

	static function makeRule( f : ClassField ) : DispatchRule {
		switch( Context.follow(f.type) ) {
		case TFun(pl, _):
			var params = [];
			var rules = [];
			var args = null, argsOpt = false;
			for( p in pl ) {
				if( p.name == "args" ) {
					if( args != null )
						Context.error("Duplicate arguments", f.pos);
					argsOpt = p.opt;
					args = makeArgs(p.t,f.pos);
					continue;
				}
				if( args != null ) Context.error("Arguments should be last parameter", f.pos);
				var r = getType(p.t, f.pos);
				if( p.opt ) r = MROpt(r);
				rules.push(r);
			}
			var rule = if( rules.length == 1 )
				DRMatch(rules[0]);
			else
				DRMult(rules);
			if( args != null )
				rule = DRArgs(rule, args, argsOpt);
			return rule;
		default:
			Context.error("Configuration entry should be a function", f.pos);
		}
		return null;
	}

	static function makeConfig( obj : Expr ) {
		var p = obj.pos;
		var t = Context.typeof(obj);
		switch( Context.follow(t) ) {
		case TAnonymous(fl):<commit_msg>return empty config in display mode<commit_after>/*
 * Copyright (c) 2005-2008, The haXe Project Contributors
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
package haxe.web;

#if macro
import haxe.macro.Expr;
import haxe.macro.Type.ClassField;
import haxe.macro.Context;
#end

typedef DispatchConfig = {
	var obj : Dynamic;
	var rules : Dynamic;
}

typedef Lock<T> = T;

enum MatchRule {
	MRInt;
	MRBool;
	MRFloat;
	MRString;
	MRDispatch;
	MRSpod( c : String, lock : Bool );
	MROpt( r : MatchRule );
}

enum DispatchRule {
	DRMatch( r : MatchRule );
	DRMult( r : Array<MatchRule> );
	DRArgs( r : DispatchRule, args : Array<{ name : String, rule : MatchRule, opt : Bool }>, opt : Bool );
	DRMeta( r : DispatchRule );
}

enum DispatchError {
	DENotFound( part : String );
	DEInvalidValue;
	DEMissing;
	DEMissingParam( p : String );
}

private class Redirect {
	public function new() {
	}
}

class Dispatch {

	public var parts : Array<String>;
	public var params : Hash<String>;
	public var name : String;
	public var cfg : DispatchConfig;

	public function new(url:String, params) {
		parts = url.split("/");
		if( parts[0] == "" ) parts.shift();
		this.params = params;
	}

	@:macro public function dispatch( ethis : Expr, obj : ExprRequire<{}> ) : ExprRequire<Void> {
		var p = Context.currentPos();
		var cfg = makeConfig(obj);
		return { expr : ECall({ expr : EField(ethis, "runtimeDispatch"), pos : p }, [cfg]), pos : p };
	}

	@:macro public function getParams( ethis : Expr ) : Expr {
		var p = Context.currentPos();
		if( PARAMS == null ) {
			PARAMS = new Array();
			Context.onGenerate(buildParams);
		}
		var index = PARAMS.length;
		var t = Context.typeof({ expr : EConst(CIdent("null")), pos : p });
		PARAMS.push( { p : p, t : t } );
		var call = { expr : ECall( { expr : EField(ethis, "runtimeGetParams"), pos : p }, [ { expr : EConst(CInt(Std.string(index))), pos : p } ]), pos : p };
		var rt = TPath( { pack : ["haxe", "macro"], name : "MacroType", params : [TPExpr(Context.parse("haxe.web.Dispatch.getRunParam("+index+")",p))], sub : null } );
		return { expr : EBlock([ { expr : EVars([ { name : "tmp", type : rt, expr : call } ]), pos : p }, { expr : EConst(CIdent("tmp")), pos : p } ]), pos : p };
	}

	public dynamic function onMeta( v : String, args : Null<Array<Dynamic>> ) {
	}

	function resolveName( name : String ) {
		return name;
	}

	public function runtimeDispatch( cfg : DispatchConfig ) {
		name = parts.shift();
		if( name == null )
			name = "default";
		name = resolveName(name);
		this.cfg = cfg;
		var r : DispatchRule = Reflect.field(cfg.rules, name);
		if( r == null ) {
			r = Reflect.field(cfg.rules, "default");
			if( r == null )
				throw DENotFound(name);
			parts.unshift(name);
			name = "default";
		}
		name = "do" + name.charAt(0).toUpperCase() + name.substr(1);
		var args = [];
		loop(args, r);
		try {
			Reflect.callMethod(cfg.obj, Reflect.field(cfg.obj, name), args);
		} catch( e : Redirect ) {
			runtimeDispatch(cfg);
		}
	}

	public function redirect( url : String, ?params : Hash<String> ) {
		parts = url.split("/");
		if( parts[0] == "" ) parts.shift();
		if( params != null ) this.params = params;
		throw new Redirect();
	}

	static var GET_RULES;
	public function runtimeGetParams( cfgIndex : Int ) : Dynamic {
		if( GET_RULES == null )
			GET_RULES = haxe.Unserializer.run(haxe.rtti.Meta.getType(Dispatch).getParams[0]);
		return checkParams(GET_RULES[cfgIndex], true);
	}

	function match( v : String, r : MatchRule, opt : Bool ) : Dynamic {
		switch( r ) {
		case MRInt:
			if( v == null ) throw DEMissing;
			if( opt && v == "" ) return null;
			var v = Std.parseInt(v);
			if( v == null ) throw DEInvalidValue;
			return v;
		case MRFloat:
			if( v == null ) throw DEMissing;
			if( opt && v == "" ) return null;
			var v = Std.parseFloat(v);
			if( Math.isNaN(v) ) throw DEInvalidValue;
			return v;
		case MRString:
			if( v == null ) throw DEMissing;
			return v;
		case MRBool:
			return v != null && v != "0" && v != "false" && v != "null";
		case MRDispatch:
			if( v != null )
				parts.unshift(v);
			return this;
		case MRSpod(c, lock):
			if( v == null ) throw DEMissing;
			var v = Std.parseInt(v);
			if( v == null ) throw DEInvalidValue;
			var cl : Dynamic = Type.resolveClass(c);
			if( cl == null ) throw "assert";
			var o : Dynamic;
			#if spod_macro
			o = cl.manager.unsafeGet(v, lock);
			#else
			o = cl.manager.get(v, lock);
			#end
			if( o == null ) throw DEInvalidValue;
			return o;
		case MROpt(r) :
			if( v == null )
				return null;
			return match(v, r, true);
		}
	}

	function checkParams( params : Array<{ name : String, opt : Bool, rule : MatchRule }>, opt ) {
		var po = { };
		for( p in params ) {
			var v = this.params.get(p.name);
			if( v == null ) {
				if( p.opt ) continue;
				if( opt ) return null;
				throw DEMissingParam(p.name);
			}
			Reflect.setField(po, p.name, match(v, p.rule,p.opt));
		}
		return po;
	}

	function loop( args : Array<Dynamic>, r ) {
		switch( r ) {
		case DRArgs(r, params, opt):
			loop(args, r);
			args.push( checkParams(params, opt) );
		case DRMatch(r):
			args.push(match(parts.shift(), r, false));
		case DRMult(rl):
			for( r in rl )
				args.push(match(parts.shift(), r, false));
		case DRMeta(r):
			loop(args, r);
			var c = Type.getClass(cfg.obj);
			if( c == null ) throw "assert";
			var m = Reflect.field(haxe.rtti.Meta.getFields(c), name);
			if( m == null ) throw "assert";
			for( mv in Reflect.fields(m) )
				onMeta(mv, Reflect.field(m, mv));
		}
	}

	#if macro

	static inline var PREFIX = "haxe.web.Dispatch.DispatchRule.";

	static function getType(t,p) {
		switch( Context.follow(t) ) {
		case TInst(i,_):
			switch( i.toString() ) {
			case "Int":
				return MRInt;
			case "Float":
				return MRFloat;
			case "String":
				return MRString;
			case "haxe.web.Dispatch":
				return MRDispatch;
			default:
				var c = i.get();
				var csup = c.superClass;
				while( csup != null ) {
					var name = csup.t.toString();
					if( name == "neko.db.Object" || name == "sys.db.Object" ) {
						var lock = switch( t ) {
						case TType(t, _): t.get().name == "Lock";
						default: false;
						}
						return MRSpod(i.toString(), lock);
					}
					csup = csup.t.get().superClass;
				}
				Context.error("Unsupported dispatch type '"+i.toString()+"'",p);
			}
		case TEnum(e, _):
			switch( e.toString() ) {
			case "Bool":
				return MRBool;
			default:
				Context.error("Unsupported dispatch type "+e.toString(),p);
			}
		default:
			Context.error("Unsupported dispatch type "+Std.string(t),p);
		}
		return null;
	}

	static function makeArgs( t : haxe.macro.Type, p ) {
		var args = [];
		switch( Context.follow(t) ) {
		case TAnonymous(a):
			for( f in a.get().fields ) {
				var r = getType(f.type, f.pos);
				var opt = false;
				switch( f.type ) {
				case TType(t, _):
					if( t.get().name == "Null" ) opt = true;
				default:
				}
				if( r == MRBool ) opt = true;
				args.push( { name : f.name, rule : r, opt : opt } );
			}
		default:
			Context.error("Arguments should be an anonymous object", p);
		}
		return args;
	}

	static function makeRule( f : ClassField ) : DispatchRule {
		switch( Context.follow(f.type) ) {
		case TFun(pl, _):
			var params = [];
			var rules = [];
			var args = null, argsOpt = false;
			for( p in pl ) {
				if( p.name == "args" ) {
					if( args != null )
						Context.error("Duplicate arguments", f.pos);
					argsOpt = p.opt;
					args = makeArgs(p.t,f.pos);
					continue;
				}
				if( args != null ) Context.error("Arguments should be last parameter", f.pos);
				var r = getType(p.t, f.pos);
				if( p.opt ) r = MROpt(r);
				rules.push(r);
			}
			var rule = if( rules.length == 1 )
				DRMatch(rules[0]);
			else
				DRMult(rules);
			if( args != null )
				rule = DRArgs(rule, args, argsOpt);
			return rule;
		default:
			Context.error("Configuration entry should be a function", f.pos);
		}
		return null;
	}

	static function makeConfig( obj : Expr ) {
		var p = obj.pos;
		if( Context.defined("display") )
			return { expr :  EObjectDecl([ { field : "obj", expr : obj }, { field : "rules", expr : { expr : EObjectDecl([]), pos : p } } ]), pos : p };
		var t = Context.typeof(obj);
		switch( Context.follow(t) ) {
		case TAnonymous(fl):
{'fraction_non_alphanumeric': 0.14853057982525814, 'fraction_numerical': 0.0016944665078104317, 'mean_word_length': 3.104759834818518, 'pattern_counts': {'":': 10, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6685522', 'n_tokens_mistral': 7494, 'n_tokens_neox': 6480, 'n_words': 1982}