Skip to content

Commit

Permalink
Merge pull request #1754 from rejectedsoftware/issue_1753_before_on_c…
Browse files Browse the repository at this point in the history
…lass

Handle "before" annotations on REST interface classes.
  • Loading branch information
s-ludwig committed May 7, 2017
2 parents 8e84170 + e46390c commit d2ec9d1
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 12 deletions.
9 changes: 5 additions & 4 deletions utils/vibe/internal/meta/funcattr.d
Expand Up @@ -12,6 +12,7 @@
module vibe.internal.meta.funcattr;

import std.traits : isInstanceOf, ReturnType;
import vibe.internal.meta.traits : RecursiveFunctionAttributes;

/// example
unittest
Expand Down Expand Up @@ -184,7 +185,7 @@ auto computeAttributedParameter(alias FUNCTION, string NAME, ARGS...)(ARGS args)
{
import std.typetuple : Filter;
static assert(IsAttributedParameter!(FUNCTION, NAME), "Missing @before attribute for parameter "~NAME);
alias input_attributes = Filter!(isInputAttribute, __traits(getAttributes, FUNCTION));
alias input_attributes = Filter!(isInputAttribute, RecursiveFunctionAttributes!FUNCTION);
foreach (att; input_attributes)
static if (att.parameter == NAME) {
return att.evaluator(args);
Expand All @@ -207,7 +208,7 @@ auto computeAttributedParameterCtx(alias FUNCTION, string NAME, T, ARGS...)(T ct
else
import std.typetuple : AliasSeq = TypeTuple, Filter;
static assert(IsAttributedParameter!(FUNCTION, NAME), "Missing @before attribute for parameter "~NAME);
alias input_attributes = Filter!(isInputAttribute, __traits(getAttributes, FUNCTION));
alias input_attributes = Filter!(isInputAttribute, RecursiveFunctionAttributes!FUNCTION);
foreach (att; input_attributes)
static if (att.parameter == NAME) {
static if (!__traits(isStaticFunction, att.evaluator)) {
Expand Down Expand Up @@ -255,7 +256,7 @@ ReturnType!FUNCTION evaluateOutputModifiers(alias FUNCTION, ARGS...)(ReturnType!
import std.typetuple : Filter;
import vibe.internal.meta.typetuple : Compare, Group;

alias output_attributes = Filter!(isOutputAttribute, __traits(getAttributes, FUNCTION));
alias output_attributes = Filter!(isOutputAttribute, RecursiveFunctionAttributes!FUNCTION);
foreach (OA; output_attributes) {
import std.typetuple : TypeTuple;

Expand Down Expand Up @@ -379,7 +380,7 @@ private {

private alias attributes = Filter!(
isInputAttribute,
__traits(getAttributes, Function)
RecursiveFunctionAttributes!Function
);

private alias parameter_names = ParameterIdentifierTuple!Function;
Expand Down
72 changes: 71 additions & 1 deletion utils/vibe/internal/meta/traits.d
Expand Up @@ -240,7 +240,10 @@ template isPublicMember(T, string M)
static if (!__traits(compiles, TypeTuple!(__traits(getMember, T, M)))) enum isPublicMember = false;
else {
alias MEM = TypeTuple!(__traits(getMember, T, M));
enum isPublicMember = __traits(getProtection, MEM).among("public", "export");
static if (__traits(compiles, __traits(getProtection, MEM)))
enum isPublicMember = __traits(getProtection, MEM).among("public", "export");
else
enum isPublicMember = true;
}
}

Expand Down Expand Up @@ -398,3 +401,70 @@ unittest {
static assert(is(StripHeadConst!(const(immutable(int))) == int));
static assert(is(StripHeadConst!(const(int[])) == const(int)[]));
}

template derivedMethod(C, alias method)
{
import std.traits : FunctionTypeOf, MemberFunctionsTuple, ParameterTypeTuple;
import std.meta : AliasSeq;

enum fname = __traits(identifier, method);
alias overloads = MemberFunctionsTuple!(C, fname);
alias PTypes = ParameterTypeTuple!method;

template impl(size_t i) {
static if (i >= overloads.length)
alias impl = AliasSeq!();
else {
alias FT = FunctionTypeOf!(overloads[i]);
static if (__traits(compiles, FT(PTypes.init)))
alias impl = overloads[i];
else
alias impl = impl!(i+1);
}
}

alias derivedMethod = impl!0;
}

template RecursiveFunctionAttributes(alias func)
{
import std.meta : AliasSeq, staticMap;
import std.traits : BaseTypeTuple;

static if (is(AliasSeq!(__traits(parent, func))[0])) {
alias C = AliasSeq!(__traits(parent, func))[0];
template rimpl(T) {
alias DF = derivedMethod!(T, func);
static if (AliasSeq!(DF).length > 0)
alias rimpl = RecursiveFunctionAttributes!DF;
else alias rimpl = AliasSeq!();
}
alias RecursiveFunctionAttributes = AliasSeq!(
__traits(getAttributes, func),
staticMap!(rimpl, BaseTypeTuple!C)
);
} else {
alias RecursiveFunctionAttributes = AliasSeq!(__traits(getAttributes, func));
}
}

unittest {
interface I {
@(1) void test();
}

interface J {
@(4) void test(int);
}

class C : I, J {
override @(2) void test() {}
override void test(int) {}
}

class D : C {
override @(3) void test() {}
}

static assert([RecursiveFunctionAttributes!(D.test)] == [3, 2, 1]);
}
22 changes: 20 additions & 2 deletions web/vibe/web/internal/rest/common.d
Expand Up @@ -30,6 +30,7 @@ import std.meta : anySatisfy, Filter;
import std.typetuple : TypeTuple;
import vibe.inet.url : URL;
import vibe.internal.meta.funcattr : IsAttributedParameter;
import vibe.internal.meta.traits : derivedMethod;
import vibe.internal.meta.uda;

/// The settings used to generate the interface
Expand Down Expand Up @@ -249,6 +250,7 @@ import std.meta : anySatisfy, Filter;
static import std.traits;
import vibe.web.auth : AuthInfo;
import std.algorithm.searching : any, count;
import std.meta : AliasSeq;

assert(__ctfe);

Expand All @@ -262,6 +264,11 @@ import std.meta : anySatisfy, Filter;
StaticRoute route;
route.functionName = __traits(identifier, func);

static if (!is(TImpl == I))
alias cfunc = derivedMethod!(TImpl, func);
else
alias cfunc = func;

alias FuncType = FunctionTypeOf!func;
alias ParameterTypes = ParameterTypeTuple!FuncType;
alias ReturnType = std.traits.ReturnType!FuncType;
Expand Down Expand Up @@ -296,9 +303,11 @@ import std.meta : anySatisfy, Filter;
}

// determine parameter source/destination
if (is(PT == AUTHTP)) {
static if (is(PT == AUTHTP)) {
pi.kind = ParameterKind.auth;
} else if (IsAttributedParameter!(func, pname)) {
} else static if (IsAttributedParameter!(func, pname)) {
pi.kind = ParameterKind.attributed;
} else static if (AliasSeq!(cfunc).length > 0 && IsAttributedParameter!(cfunc, pname)) {
pi.kind = ParameterKind.attributed;
} else static if (anySatisfy!(mixin(CompareParamName.Name), WPAT)) {
alias PWPAT = Filter!(mixin(CompareParamName.Name), WPAT);
Expand Down Expand Up @@ -719,3 +728,12 @@ unittest {
static assert(!__traits(compiles, RestInterface!I2.init));
static assert(!__traits(compiles, RestInterface!I3.init));
}

unittest {
import vibe.http.server : HTTPServerResponse, HTTPServerRequest;
int foocomp(HTTPServerRequest, HTTPServerResponse) { return 42; }
interface I { void test(int foo); }
class C : I { @before!foocomp("foo") void test(int foo) { assert(foo == 42); }}
alias RI = RestInterface!C;
static assert(RI.staticRoutes[0].parameters[0].kind == ParameterKind.attributed);
}
15 changes: 10 additions & 5 deletions web/vibe/web/rest.d
Expand Up @@ -910,7 +910,7 @@ unittest {
long getHeaderCount(size_t foo = 0);
}

size_t handler(HTTPServerRequest req, HTTPServerResponse res)
static size_t handler(HTTPServerRequest req, HTTPServerResponse res)
{
return req.headers.length;
}
Expand Down Expand Up @@ -951,7 +951,7 @@ unittest {
long getMagic();
}

long handler(long ret, HTTPServerRequest req, HTTPServerResponse res)
static long handler(long ret, HTTPServerRequest req, HTTPServerResponse res)
{
return ret * 2;
}
Expand Down Expand Up @@ -1003,17 +1003,22 @@ unittest {
*/
private HTTPServerRequestDelegate jsonMethodHandler(alias Func, size_t ridx, T)(T inst, ref RestInterface!T intf)
{
import std.meta : AliasSeq;
import std.string : format;
import vibe.http.server : HTTPServerRequest, HTTPServerResponse;
import vibe.http.common : HTTPStatusException, HTTPStatus, enforceBadRequest;
import vibe.utils.string : sanitizeUTF8;
import vibe.web.internal.rest.common : ParameterKind;
import vibe.internal.meta.funcattr : IsAttributedParameter, computeAttributedParameterCtx;
import vibe.internal.meta.traits : derivedMethod;
import vibe.textfilter.urlencode : urlDecode;

enum Method = __traits(identifier, Func);
alias PTypes = ParameterTypeTuple!Func;
alias PDefaults = ParameterDefaultValueTuple!Func;
alias CFuncRaw = derivedMethod!(T, Func);
static if (AliasSeq!(CFuncRaw).length > 0) alias CFunc = CFuncRaw;
else alias CFunc = Func;
alias RT = ReturnType!(FunctionTypeOf!Func);
static const sroute = RestInterface!T.staticRoutes[ridx];
auto route = intf.routes[ridx];
Expand Down Expand Up @@ -1063,9 +1068,9 @@ private HTTPServerRequestDelegate jsonMethodHandler(alias Func, size_t ridx, T)(
if (auto pv = fieldname in req.headers)
v = fromRestString!PT(*pv);
} else static if (sparam.kind == ParameterKind.attributed) {
static if (!__traits(compiles, () @safe { computeAttributedParameterCtx!(Func, pname)(inst, req, res); } ()))
static if (!__traits(compiles, () @safe { computeAttributedParameterCtx!(CFunc, pname)(inst, req, res); } ()))
pragma(msg, "Non-@safe @before evaluators are deprecated - annotate evaluator function for parameter "~pname~" of "~T.stringof~"."~Method~" as @safe.");
v = () @trusted { return computeAttributedParameterCtx!(Func, pname)(inst, req, res); } ();
v = () @trusted { return computeAttributedParameterCtx!(CFunc, pname)(inst, req, res); } ();
} else static if (sparam.kind == ParameterKind.internal) {
if (auto pv = fieldname in req.params)
v = fromRestString!PT(urlDecode(*pv));
Expand Down Expand Up @@ -1132,7 +1137,7 @@ private HTTPServerRequestDelegate jsonMethodHandler(alias Func, size_t ridx, T)(
static if (!__traits(compiles, () @safe { evaluateOutputModifiers!Func(ret, req, res); } ()))
pragma(msg, "Non-@safe @after evaluators are deprecated - annotate @after evaluator function for "~T.stringof~"."~Method~" as @safe.");

ret = () @trusted { return evaluateOutputModifiers!Func(ret, req, res); } ();
ret = () @trusted { return evaluateOutputModifiers!CFunc(ret, req, res); } ();
returnHeaders();
debug res.writePrettyJsonBody(ret);
else res.writeJsonBody(ret);
Expand Down

0 comments on commit d2ec9d1

Please sign in to comment.