Skip to content

Instantly share code, notes, and snippets.

@ciscoheat
Last active August 26, 2016 19:51
Show Gist options
  • Save ciscoheat/6b95e09f851b28ea3db9dc87314cca9f to your computer and use it in GitHub Desktop.
Save ciscoheat/6b95e09f851b28ea3db9dc87314cca9f to your computer and use it in GitHub Desktop.
Typesafe web routes with Haxe in less than 100 lines
// Code for this article:
// https://github.com/ciscoheat/mithril-hx/wiki/Typesafe-web-routes-with-Haxe-in-less-than-100-lines
#if macro
import haxe.Serializer;
import haxe.Unserializer;
import haxe.macro.Context;
import haxe.macro.Expr;
import sys.io.File;
using StringTools;
using Lambda;
private typedef RouteList = Array<Array<String>>;
private typedef RouteCalls = Map<{min: Int, max: Int, file: String}, Array<String>>;
#end
class Routes
{
#if macro
static var isInit = false;
static var file = 'routes.txt';
static var definedRoutes(default, null) : RouteList;
static var calledRoutes(default, null) : RouteCalls;
static function testCalledRoutes() {
// Load routes if they haven't been defined, else save the current routes
// this enables the class to work on both server and client.
if(definedRoutes.length == 0)
definedRoutes = cast Unserializer.run(File.getContent(file));
else
File.saveContent(file, Serializer.run(definedRoutes));
var joinedRoutes = definedRoutes.map(function(a) return a.join('/'));
for (pos in calledRoutes.keys()) {
var route = calledRoutes.get(pos);
var matched = joinedRoutes.has(route.join('/'));
if (!matched)
Context.error('Route not found', Context.makePosition(pos));
}
}
static function init() {
isInit = true;
definedRoutes = new RouteList();
calledRoutes = new RouteCalls();
Context.onAfterGenerate(testCalledRoutes);
}
#end
macro public static function defineRoute(route : String) : Expr {
if (!isInit) init();
var array = route.split('/');
if (array[0].length == 0) array.shift();
function mapRoute(a : Array<String>) {
definedRoutes.push(a.map(function(s) return s.startsWith(':') ? '' : s));
}
mapRoute(array);
while (array.length > 0 && array[array.length - 1].endsWith('?')) {
array.pop();
mapRoute(array);
}
return macro $v{route};
}
macro public static function callRoute(values : Array<Expr>) : Expr {
if (!isInit) init();
var route = values.map(function(e) return switch(e.expr) {
case EConst(CString(s)): s;
case _: '';
});
if (route[0].length == 0) route.shift();
calledRoutes.set(Context.getPosInfos(Context.currentPos()), route);
return macro '/' + $a{values}.join('/');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment