-
-
Save AlexHaxe/7a2618835b3b24d10ce7bc103f2e20dd to your computer and use it in GitHub Desktop.
Paths example
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
import haxe.Int64; | |
class Main { | |
static function main() { | |
printPage(["Home"]); | |
printPage(["Foo", Boo, "1"]); | |
printPage(["Foo", "hii", "1"]); | |
printPage(["Foo64", "hooo", "1234567890123456"]); | |
printPage(["Scales", "ukulele", "Chromatic"]); | |
printPage(["Custom", "Path", "To", "Content"]); | |
printPage(["Foo", "xxx", "1"]); | |
trace(Foo(Boo, 10) + "-> /" + Paths.toPath(Foo(Boo, 10))); | |
trace(Scales(Ukulele, Ionian) + "-> /" + Paths.toPath(Scales(Ukulele, Ionian))); | |
trace(Foo("xxx", 10) + "-> /" + Paths.toPath(Foo("xxx", 10))); | |
} | |
static function printPage(parts:Array<String>) { | |
var router = Paths.buildRouter(Page); | |
var page = router(parts.copy()); | |
trace('${parts.map(p -> '"$p"')} -> $page'); | |
trace('$page -> "/${Paths.toPath(page)}"'); | |
} | |
} | |
enum abstract Bar(String) from String to String { | |
var Baz = 'heeey'; | |
var Bing = 'hii'; | |
var Boo = 'hooo'; | |
} | |
enum Page { | |
Home; | |
Foo(bar:Bar, val:Int); | |
Foo64(bar:Bar, val:Int64); | |
Scales(instrument:GuitarMember, scale:Scale); | |
Intervals(instrument:GuitarMember, scale:Scale, key:Note); | |
ChordProgressionPage(instrument:GuitarMember, scale:Scale, key:Note, highlighted:Scale); | |
SuspendedChordsPage(instrument:GuitarMember, scale:Scale, key:Note, highlighted:Scale); | |
PowerChordsPage(instrument:GuitarMember, scale:Scale, key:Note, highlighted:Scale); | |
ScaleNotesPage(instrument:GuitarMember, scale:Scale, key:Note); | |
ChordNotesPage(instrument:GuitarMember, scale:Scale, key:Note); | |
NoteOverviewPage(instrument:GuitarMember, key:Note); | |
@catchrest CatchRest(parts:String); | |
} | |
@:enum abstract GuitarMember(String) from String to String { | |
var Guitar = "guitar"; | |
var Ukulele = "ukulele"; | |
var BassGuitar = "bass-guitar"; | |
var Banjo = "banjo"; | |
var Mandolin = "mandolin"; | |
} | |
@:enum abstract Scale(String) from String to String { | |
var Chromatic = "chromatic"; | |
var NaturalMinor = "natural-minor"; | |
var NaturalMajor = "natural-major"; | |
var MinorPentatonic = "minor-pentatonic"; | |
var MajorPentatonic = "major-pentatonic"; | |
var MelodicMinor = "melodic-minor"; | |
var HarmonicMinor = "harmonic-minor"; | |
var Blues = "blues"; | |
var Ionian = "ionian"; | |
var Dorian = "dorian"; | |
var Phygian = "phygian"; | |
var Lydian = "lydian"; | |
var Mixolydian = "mixolydian"; | |
var Aeolian = "aeolian"; | |
var Locrian = "locrian"; | |
} | |
@:enum abstract Note(String) from String to String { | |
/* 1 */ var C = "e"; | |
/* 2 */ var CSharp = "c-sharp"; | |
/* 3 */ var D = "d"; | |
/* 4 */ var DSharp = "d-sharp"; | |
/* 5 */ var E = "e"; | |
/* 6 */ var F = "f"; | |
/* 7 */ var FSharp = "f-sharp"; | |
/* 8 */ var G = "g"; | |
/* 9 */ var GSharp = "g-sharp"; | |
/* 10 */ var A = "a"; | |
/* 11 */ var ASharp = "a-sharp"; | |
/* 12 */ var B = "b"; | |
} |
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
src/Main.hx:24: ["Home"] -> Home | |
src/Main.hx:25: Home -> "/Home" | |
src/Main.hx:24: ["Foo","hooo","1"] -> Foo(hooo,1) | |
src/Main.hx:25: Foo(hooo,1) -> "/Foo/hooo/1" | |
src/Main.hx:24: ["Foo","hii","1"] -> Foo(hii,1) | |
src/Main.hx:25: Foo(hii,1) -> "/Foo/hii/1" | |
src/Main.hx:24: ["Foo64","hooo","1234567890123456"] -> Foo64(hooo,haxe._Int64.___Int64) | |
src/Main.hx:25: Foo64(hooo,haxe._Int64.___Int64) -> "/Foo64/hooo/1234567890123456" | |
src/Main.hx:24: ["Scales","ukulele","Chromatic"] -> Scales(ukulele,Chromatic) | |
src/Main.hx:25: Scales(ukulele,Chromatic) -> "/Scales/ukulele/Chromatic" | |
src/Main.hx:24: ["Custom","Path","To","Content"] -> CatchRest(Custom/Path/To/Content) | |
src/Main.hx:25: CatchRest(Custom/Path/To/Content) -> "/Custom/Path/To/Content" | |
src/Main.hx:24: ["Foo","xxx","1"] -> Foo(xxx,1) | |
src/Main.hx:25: Foo(xxx,1) -> "/Foo/xxx/1" | |
src/Main.hx:14: Foo(hooo,10)-> /Foo/hooo/10 | |
src/Main.hx:15: Scales(ukulele,ionian)-> /Scales/ukulele/ionian | |
src/Main.hx:17: Foo(xxx,10)-> /Foo/xxx/10 |
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; | |
#end | |
class Paths { | |
macro public static function buildRouter(enm:Expr):Expr { | |
return macro function(paths:Array<String>) { | |
var catchRest:String = paths.join("/"); | |
return ${buildSwitchExpr(enm)} | |
}; | |
} | |
macro public static function toPath(value) { | |
var en:EnumType = switch Context.follow(Context.typeof(value)) { | |
case TEnum(_.get() => en, _): en; | |
case _: throw new Error("not an enum", value.pos); | |
} | |
var cases:Array<Case> = []; | |
for (ctorName in en.names) { | |
var field:EnumField = en.constructs[ctorName]; | |
var ctorIdent:Expr = macro $i{ctorName}; | |
var urlParts:Array<Expr> = [macro $v{ctorName}]; | |
if (field.meta.extract("catchrest").length > 0) { | |
urlParts = []; | |
} | |
var path_override:String = detectOverridePath(field, ctorName, urlParts); | |
switch (field.type) { | |
case TEnum(_): | |
cases.push({ | |
values: [ctorIdent], | |
expr: macro $v{ctorName} | |
}); | |
case TFun(args, _): | |
var capturedNames:Array<Expr> = []; | |
for (arg in args) { | |
argsToPath(arg, path_override, urlParts, capturedNames); | |
} | |
cases.push({ | |
values: [macro $ctorIdent($a{capturedNames})], | |
expr: macro $a{urlParts}.join("/") | |
}); | |
case _: | |
throw "assert"; | |
} | |
} | |
return { | |
pos: Context.currentPos(), | |
expr: ESwitch(value, cases, null) | |
}; | |
} | |
#if macro | |
private static function buildExprFromType(type:Type, idx = 0):Expr { | |
return switch (Context.followWithAbstracts(type)) { | |
case TInst(t, params) if (t.get().module == "StdTypes"): | |
switch (t.get().name) { | |
case "String": | |
macro paths.shift(); | |
case "Int": | |
macro Std.parseInt(paths.shift()); | |
case "Float": | |
macro Std.parseFloat(paths.shift()); | |
default: | |
macro null; | |
} | |
case TInst(t, params): | |
switch (t.get().module) { | |
case "haxe.Int64": | |
macro Int64.parseString(paths.shift()); | |
default: | |
macro paths.shift(); | |
} | |
case TEnum(_, _): | |
buildSwitchFromType(type); | |
case TAbstract(abs, []) if (abs.get().module == "StdTypes"): | |
switch (abs.get().name) { | |
case "String": | |
macro paths.shift(); | |
case "Int": | |
macro Std.parseInt(paths.shift()); | |
case "Float": | |
macro Std.parseFloat(paths.shift()); | |
default: | |
macro null; | |
} | |
case TAbstract(abs, []): | |
var impl:ClassType = abs.get().impl.get(); | |
buildSwitchFromAbsImpl(impl); | |
default: | |
macro null; | |
} | |
} | |
private static function buildCaseFromEnumField(enmf:EnumField):Case { | |
return switch (enmf.type) { | |
case TEnum(enm, []): | |
return { | |
values: [macro $v{enmf.name}], | |
expr: macro $i{enmf.name} | |
} | |
case TFun(args, ret): | |
var arg_exprs:Array<Expr> = Lambda.map(args, a -> buildExprFromType(a.t)); | |
return { | |
values: [macro $v{enmf.name}], | |
expr: macro $i{enmf.name}($a{arg_exprs}) | |
} | |
default: | |
null; | |
}; | |
} | |
private static function buildSwitchFromAbsImpl(impl:ClassType):Expr { | |
var cases:Array<Case> = Lambda.map(impl.statics.get(), s -> { | |
values: [macro $v{s.name}], | |
expr: macro $i{s.name}, | |
guard: null | |
}); | |
cases.push({ | |
values: [macro _], | |
expr: macro null, | |
guard: null | |
}); | |
return { | |
expr: ESwitch(macro paths.shift(), cases, null), | |
pos: Context.currentPos() | |
}; | |
} | |
private static function buildSwitchFromType(type:Type, idx = 0):Expr { | |
return switch (type) { | |
case TEnum(enm, []): | |
var enme:EnumType = enm.get(); | |
var cases:Array<Case> = []; | |
var catchRestConstruct:Null<EnumField> = null; | |
for (c in enme.constructs) { | |
if (c.meta.extract("catchrest").length > 0) { | |
catchRestConstruct = c; | |
} else { | |
cases.push(buildCaseFromEnumField(enme.constructs.get(c.name))); | |
} | |
} | |
if (catchRestConstruct == null) { | |
cases.push({ | |
values: [macro _], | |
expr: macro null | |
}); | |
} else { | |
var c:Case = { | |
values: [macro _], | |
expr: macro $i{catchRestConstruct.name}($a{[catchRest]}) | |
}; | |
cases.push(c); | |
} | |
return { | |
expr: ESwitch(macro paths.shift(), cases, null), | |
pos: Context.currentPos() | |
} | |
default: | |
macro null; | |
} | |
} | |
private static function buildSwitchExpr(expr:Expr, idx = 0):Expr { | |
return switch (expr.expr) { | |
case EConst(CIdent(val)): | |
buildSwitchFromType(Context.getType(val)); | |
case _: | |
throw new Error("not an enum", Context.currentPos()); | |
} | |
} | |
private static function detectOverridePath(field:EnumField, ctorName:String, urlParts:Array<Expr>):String { | |
if (field.meta.has("path")) { | |
var k:MetadataEntry = field.meta.extract("path")[0]; | |
var path_expr:ExprDef = k.params[0].expr; | |
return switch (path_expr) { | |
case EConst(CString(str)): | |
str; | |
case _: | |
urlParts = [macro $v{ctorName}]; | |
null; | |
} | |
}; | |
return null; | |
} | |
private static function pathWithOverride(path_override:String, argIdent:Expr):Expr { | |
if (path_override == null) { | |
return argIdent; | |
} else { | |
return macro $v{path_override}; | |
} | |
} | |
private static function argsToPath(arg:{name:String, opt:Bool, t:Type}, path_override:String, urlParts:Array<Expr>, capturedNames:Array<Expr>) { | |
switch (Context.follow(arg.t)) { | |
case TInst(ct, []): | |
var argIdent:Expr = macro $i{arg.name}; | |
capturedNames.push(argIdent); | |
urlParts.push(pathWithOverride(path_override, $v{argIdent})); | |
case TEnum(enm, []): | |
var argIdent:Expr = macro $i{arg.name}; | |
capturedNames.push(argIdent); | |
urlParts.push(pathWithOverride(path_override, macro PathRouter.toPath($argIdent))); | |
case TAbstract(abstr, []) if (abstr.get().module == "StdTypes"): | |
var argIdent:Expr = macro $i{arg.name}; | |
capturedNames.push(argIdent); | |
var argIdent:Expr = switch (abstr.get().name) { | |
case "String": | |
macro ${argIdent}; | |
case "Int": | |
macro Std.string(${argIdent}); | |
case "Float": | |
macro Std.string(${argIdent}); | |
default: | |
macro null; | |
} | |
if (argIdent != null) { | |
urlParts.push(pathWithOverride(path_override, argIdent)); | |
} | |
case TAbstract(abstr, []): | |
var argIdent:Expr = macro $i{arg.name}; | |
capturedNames.push(argIdent); | |
var impl:ClassType = abstr.get().impl.get(); | |
var argIdent:Expr = switch (impl.module) { | |
case "haxe.Int64": | |
macro Int64.toStr(${argIdent}); | |
default: | |
macro ${argIdent}; | |
}; | |
urlParts.push(pathWithOverride(path_override, argIdent)); | |
case _: | |
} | |
} | |
#end | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment