Skip to content

Commit 6a6dc4f

Browse files
committed
Filters now is dedicated
1 parent a420ada commit 6a6dc4f

File tree

8 files changed

+416
-40
lines changed

8 files changed

+416
-40
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System.Reflection;
2+
3+
namespace Deployf.Botf;
4+
5+
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
6+
public sealed class FilterAttribute : Attribute
7+
{
8+
public readonly string Filter;
9+
public readonly BoolOp Operation;
10+
public readonly object? Param;
11+
12+
public FilterAttribute(string? Or = null, string? And = null, string? OrNot = null, string? AndNot = null, string? Not = null, object? Param = null)
13+
{
14+
var arguments = new [] { Or, And, OrNot, AndNot, Not }.Where(c => c != null);
15+
if(arguments.Count() > 1 || !arguments.Any())
16+
{
17+
throw new BotfException("You must pass only single argument into Filter() attribute");
18+
}
19+
20+
Filter = arguments.First()!;
21+
22+
if(And != null)
23+
{
24+
Operation = BoolOp.And;
25+
}
26+
else if (Or != null)
27+
{
28+
Operation = BoolOp.Or;
29+
}
30+
else if (OrNot != null)
31+
{
32+
Operation = BoolOp.OrNot;
33+
}
34+
else if (AndNot != null)
35+
{
36+
Operation = BoolOp.AndNot;
37+
}
38+
else if (Not != null)
39+
{
40+
Operation = BoolOp.Not;
41+
}
42+
43+
this.Param = Param;
44+
}
45+
46+
public MethodInfo? GetMethod(Type? declaringType) => GetMethod(Filter, declaringType);
47+
48+
public static MethodInfo? GetMethod(string filter, Type? declaringType)
49+
{
50+
if(filter.Contains('.'))
51+
{
52+
var typeName = filter.Substring(0, filter.LastIndexOf('.'));
53+
var methodName = filter.Substring(filter.LastIndexOf('.') + 1);
54+
55+
var type = Type.GetType(typeName);
56+
if(type == null)
57+
{
58+
return null;
59+
}
60+
61+
var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
62+
return method;
63+
}
64+
65+
return declaringType!.GetMethod(filter, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
66+
}
67+
68+
public enum BoolOp
69+
{
70+
Not,
71+
And,
72+
Or,
73+
AndNot,
74+
OrNot,
75+
}
76+
}

Deployf.Botf/Attributes/OnAttribute.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,34 @@ public sealed class OnAttribute : Attribute
77
public readonly string? Filter;
88
public readonly int Order;
99

10-
public OnAttribute(Handle type, string? filter = null, int order = 0)
10+
public OnAttribute(Handle type)
11+
{
12+
Handler = type;
13+
Filter = null;
14+
Order = 0;
15+
}
16+
17+
[Obsolete("Use Filter() attribute instead passing filter through On")]
18+
public OnAttribute(Handle type, string? filter)
1119
{
1220
Handler = type;
1321
Filter = filter;
22+
Order = 0;
23+
}
24+
25+
[Obsolete("Use Filter() attribute instead passing filter through On")]
26+
public OnAttribute(Handle type, string? filter, int order)
27+
{
28+
Handler = type;
29+
Filter = filter;
30+
Order = order;
31+
}
32+
33+
34+
public OnAttribute(Handle type, int order)
35+
{
36+
Handler = type;
37+
Filter = null;
1438
Order = order;
1539
}
1640
}

Deployf.Botf/System/BotControllerFactory.cs

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -77,40 +77,103 @@ public static BotControllerHandlers MakeHandlers()
7777
.OrderBy(c => c.on.Filter == null)
7878
.ThenByDescending(c => c.on.Order)
7979

80-
.Select(c => new HandlerItem(c.on.Handler, c.m, GetFilter(c.m, c.on.Filter)));
80+
.Select(c => new HandlerItem(c.on.Handler, c.m, GetFilter(c.m, c.on)));
8181

8282
return new BotControllerHandlers(handlers);
8383

84-
static ActionFilter? GetFilter(MethodInfo target, string? filterMethod)
84+
static ActionFilter? GetFilter(MethodInfo target, OnAttribute on)
8585
{
86-
if(filterMethod == null)
86+
var filters = target.GetCustomAttributes<FilterAttribute>().ToArray();
87+
88+
if(filters.Length == 0 && on.Filter == null)
8789
{
8890
return null;
8991
}
9092

91-
if(filterMethod.Contains('.'))
93+
if(filters.Length != 0 && on.Filter != null)
9294
{
93-
var typeName = filterMethod.Substring(0, filterMethod.LastIndexOf('.'));
94-
var methodName = filterMethod.Substring(filterMethod.LastIndexOf('.') + 1);
95+
throw new BotfException("Use only Filter() attribute to pass filter methods of handlers");
96+
}
9597

96-
var type = Type.GetType(typeName);
97-
if(type == null)
98-
{
99-
throw new BotfException($"Filter method name is wrong. Can't find class type `{typeName}`. Action method is `{target.DeclaringType!.Name}.{target.Name}`");
100-
}
98+
if(on.Filter != null)
99+
{
100+
var methodInTarget = FilterAttribute.GetMethod(on.Filter, target.DeclaringType);
101+
CheckFilterMethod(methodInTarget, on.Filter, target.DeclaringType!.Name);
102+
return (ActionFilter)ActionFilter.CreateDelegate(typeof(ActionFilter), methodInTarget!);
103+
}
104+
105+
if(filters.Length == 0)
106+
{
107+
return null;
108+
}
101109

102-
var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
103-
CheckFilterMethod(method, methodName, typeName, target);
110+
ActionFilter? result = null;
111+
112+
for (int i = 0; i < filters.Length; i++)
113+
{
114+
var filter = filters[i];
115+
var method = filter.GetMethod(target.DeclaringType);
116+
CheckFilterMethod(method, filter.Filter, target.DeclaringType!.Name);
117+
var action = (ActionFilter)ActionFilter.CreateDelegate(typeof(ActionFilter), method!);
104118

105-
return (ActionFilter)ActionFilter.CreateDelegate(typeof(ActionFilter), method!);
119+
if(result == null)
120+
{
121+
if(filter.Operation == FilterAttribute.BoolOp.Not)
122+
{
123+
result = (IUpdateContext ctx) =>
124+
{
125+
ctx.SetFilterParameter(filter.Param);
126+
return !action(ctx);
127+
};
128+
}
129+
else
130+
{
131+
result = (IUpdateContext ctx) =>
132+
{
133+
ctx.SetFilterParameter(filter.Param);
134+
return action(ctx);
135+
};
136+
}
137+
}
138+
else
139+
{
140+
var currentResult = result;
141+
result = (IUpdateContext ctx) =>
142+
{
143+
var leftResult = currentResult(ctx);
144+
145+
ctx.SetFilterParameter(filter.Param);
146+
var rightResult = action(ctx);
147+
148+
if(filter.Operation == FilterAttribute.BoolOp.And)
149+
{
150+
return leftResult && rightResult;
151+
}
152+
else if(filter.Operation == FilterAttribute.BoolOp.Or)
153+
{
154+
return leftResult || rightResult;
155+
}
156+
else if(filter.Operation == FilterAttribute.BoolOp.AndNot)
157+
{
158+
return leftResult && !rightResult;
159+
}
160+
else if(filter.Operation == FilterAttribute.BoolOp.OrNot)
161+
{
162+
return leftResult || !rightResult;
163+
}
164+
else if(filter.Operation == FilterAttribute.BoolOp.Not)
165+
{
166+
throw new NotSupportedException($"Operation NOT is supported only for first filter");
167+
}
168+
169+
throw new NotSupportedException($"Operation type {filter.Operation} is not supported");
170+
};
171+
}
106172
}
107-
108-
var methodInTarget = target.DeclaringType!.GetMethod(filterMethod, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
109-
CheckFilterMethod(methodInTarget, filterMethod, target.DeclaringType.Name, target);
110173

111-
return (ActionFilter)ActionFilter.CreateDelegate(typeof(ActionFilter), methodInTarget!);
174+
return result;
112175

113-
static void CheckFilterMethod(MethodInfo? filter, string methodName, string typeName, MethodInfo target)
176+
static void CheckFilterMethod(MethodInfo? filter, string methodName, string typeName)
114177
{
115178
if(filter == null
116179
|| filter.ReturnType != typeof(bool)

Deployf.Botf/System/BotControllerRoutes.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ public bool TryFilter(IUpdateContext context)
121121
return true;
122122
}
123123

124+
context.SetCurrentHandler(this);
125+
124126
return Filter(context);
125127
}
126128
}
@@ -165,13 +167,7 @@ public IEnumerable<MethodInfo> TryFindHandlers(Handle handle, IUpdateContext con
165167
continue;
166168
}
167169

168-
if(item.Filter == null)
169-
{
170-
yield return item.TargetMethod;
171-
continue;
172-
}
173-
174-
if(item.Filter(context))
170+
if(item.TryFilter(context))
175171
{
176172
yield return item.TargetMethod;
177173
}

0 commit comments

Comments
 (0)