Skip to content

Commit 4b97d82

Browse files
committed
chore: Improve parameter definition matching
1 parent dc38ae8 commit 4b97d82

File tree

4 files changed

+51
-66
lines changed

4 files changed

+51
-66
lines changed

packages/spanner/lib/src/parametric/definition.dart

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@ import 'utils.dart';
44

55
typedef ParamAndValue = ({String name, String? value});
66

7-
SingleParameterDefn _singleParamDefn(RegExpMatch m, [String? nextPart]) =>
8-
SingleParameterDefn._(
7+
SingleParameterDefn _singleParamDefn(RegExpMatch m) => SingleParameterDefn._(
98
m.group(2)!,
109
prefix: m.group(1)?.nullIfEmpty,
1110
suffix: m.group(3)?.nullIfEmpty,
12-
nextPart: nextPart,
1311
);
1412

15-
ParameterDefinition buildParamDefinition(String part, [String? nextPart]) {
13+
ParameterDefinition buildParamDefinition(String part) {
1614
if (closeDoorParametricRegex.hasMatch(part)) {
1715
throw ArgumentError.value(
1816
part, null, 'Parameter definition is invalid. Close door neighbors');
@@ -24,13 +22,10 @@ ParameterDefinition buildParamDefinition(String part, [String? nextPart]) {
2422
}
2523

2624
if (matches.length == 1) {
27-
return _singleParamDefn(matches.first, nextPart);
25+
return _singleParamDefn(matches.first);
2826
}
2927

30-
return CompositeParameterDefinition._(
31-
matches.map(_singleParamDefn),
32-
nextPart,
33-
);
28+
return CompositeParameterDefinition._(matches.map(_singleParamDefn));
3429
}
3530

3631
abstract class ParameterDefinition implements HandlerStore {
@@ -42,9 +37,6 @@ abstract class ParameterDefinition implements HandlerStore {
4237

4338
bool get terminal;
4439

45-
/// Next route part included so we can match early if its a static next part
46-
String? get nextPart;
47-
4840
bool matches(String route, {bool caseSensitive = false});
4941

5042
void resolveParams(
@@ -61,14 +53,11 @@ class SingleParameterDefn extends ParameterDefinition with HandlerStoreMixin {
6153
final String? prefix;
6254
final String? suffix;
6355

64-
@override
65-
final String? nextPart;
66-
6756
@override
6857
final String templateStr;
6958

7059
@override
71-
String get key => 'prefix=$prefix&suffix=$suffix&terminal=$terminal';
60+
String get key => 'prefix=$prefix&suffix=$suffix';
7261

7362
bool _terminal;
7463

@@ -77,20 +66,19 @@ class SingleParameterDefn extends ParameterDefinition with HandlerStoreMixin {
7766

7867
@override
7968
bool matches(String route, {bool caseSensitive = false}) {
80-
return matchPattern(
81-
route,
82-
prefix ?? '',
83-
suffix ?? '',
84-
caseSensitive,
85-
) !=
86-
null;
69+
final match = matchPattern(
70+
route,
71+
prefix ?? '',
72+
suffix ?? '',
73+
caseSensitive,
74+
);
75+
return match != null;
8776
}
8877

8978
SingleParameterDefn._(
9079
this.name, {
9180
this.prefix,
9281
this.suffix,
93-
this.nextPart,
9482
}) : templateStr =
9583
buildTemplateString(name: name, prefix: prefix, suffix: suffix),
9684
_terminal = false;
@@ -119,11 +107,7 @@ class CompositeParameterDefinition extends ParameterDefinition
119107
final Iterable<SingleParameterDefn> parts;
120108
final SingleParameterDefn _maybeTerminalPart;
121109

122-
@override
123-
final String? nextPart;
124-
125-
CompositeParameterDefinition._(this.parts, [this.nextPart])
126-
: _maybeTerminalPart = parts.last;
110+
CompositeParameterDefinition._(this.parts) : _maybeTerminalPart = parts.last;
127111

128112
@override
129113
String get templateStr => parts.map((e) => e.templateStr).join();

packages/spanner/lib/src/tree/node.dart

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -140,17 +140,26 @@ class ParametricNode extends Node {
140140

141141
ParametricNode(HTTPMethod method, ParameterDefinition defn)
142142
: _definitionsMap = {} {
143-
addNewDefinition(method, defn);
143+
addNewDefinition(method, defn, false);
144144
}
145145

146-
void addNewDefinition(HTTPMethod method, ParameterDefinition defn) {
146+
void addNewDefinition(
147+
HTTPMethod method,
148+
ParameterDefinition defn,
149+
bool terminal,
150+
) {
147151
var definitions = _definitionsMap[method];
148152
if (definitions == null) {
149153
definitions = [];
150154
_definitionsMap[method] = definitions;
151155
}
152156

153157
if (definitions.isNotEmpty) {
158+
/// At this point, terminal in [defn] will always be false since
159+
/// terminals are only set after we've added a route to the definition.
160+
///
161+
/// So we compare key without terminal and then manually check with the
162+
/// [terminal] value we have right now.
154163
final existing = definitions.firstWhereOrNull((e) => e.key == defn.key);
155164
if (existing != null) {
156165
if (existing.name != defn.name) {
@@ -162,13 +171,8 @@ class ParametricNode extends Node {
162171
);
163172
}
164173

165-
if (existing.terminal && defn.terminal) {
166-
throw ArgumentError(
167-
'Route already exists${[
168-
' - ${existing.templateStr}',
169-
' - ${defn.templateStr}',
170-
].join('\n')}',
171-
);
174+
if (existing.terminal && terminal) {
175+
throw ArgumentError('Route entry already exists');
172176
}
173177

174178
return;
@@ -191,19 +195,9 @@ class ParametricNode extends Node {
191195
String? nextPart,
192196
}) {
193197
return _definitionsMap[method]?.firstWhereOrNull(
194-
(e) {
195-
if (nextPart != null && e.nextPart != null && e.nextPart!.isStatic) {
196-
final match = caseSensitive
197-
? e.nextPart == nextPart
198-
: e.nextPart!.toLowerCase() == nextPart.toLowerCase();
199-
if (match) return true;
200-
}
201-
202-
return (terminal
203-
? (e.terminal && e.hasMethod(method))
204-
: e.terminal == terminal) &&
205-
e.matches(part, caseSensitive: caseSensitive);
206-
},
198+
(e) =>
199+
(!terminal || (e.terminal && e.hasMethod(method))) &&
200+
e.matches(part, caseSensitive: caseSensitive),
207201
);
208202
}
209203
}

packages/spanner/lib/src/tree/tree.dart

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class Spanner {
146146
return node.addChildAndReturn(WildcardNode.key, WildcardNode());
147147
}
148148

149-
final defn = buildParamDefinition(routePart, nextPart);
149+
final defn = buildParamDefinition(routePart);
150150
final paramNode = node.paramNode;
151151

152152
if (paramNode == null) {
@@ -157,7 +157,7 @@ class Spanner {
157157
return nextPart == null ? defn : newNode;
158158
}
159159

160-
paramNode.addNewDefinition(method, defn);
160+
paramNode.addNewDefinition(method, defn, nextPart == null);
161161

162162
return nextPart == null
163163
? defn
@@ -218,13 +218,7 @@ class Spanner {
218218
/// use wildcard if we have any registered
219219
if ((childNode == null && parametricNode == null) ||
220220
(childNode == null && definition == null)) {
221-
if (wildcardNode != null) {
222-
resolvedParams.add((
223-
name: WildcardNode.key,
224-
value: routeSegments.sublist(i).join('/'),
225-
));
226-
rootNode = wildcardNode;
227-
}
221+
if (wildcardNode != null) rootNode = wildcardNode;
228222
break;
229223
}
230224

packages/spanner/test/parametric_test.dart

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,21 @@ void main() {
4545
..addRoute(HTTPMethod.GET, '/user', null)
4646
..addRoute(HTTPMethod.GET, '/user', null);
4747

48-
final exception = runSyncAndReturnException<ArgumentError>(router);
48+
router2() => Spanner()
49+
..addRoute(HTTPMethod.GET, '/<lang>/item/<id>', null)
50+
..addRoute(HTTPMethod.GET, '/<lang>/item/<id>', null);
51+
52+
router3() => Spanner()
53+
..addRoute(HTTPMethod.GET, '/<lang>/item/chima<id>hello', null)
54+
..addRoute(HTTPMethod.GET, '/<lang>/item/chima<id>hello', null);
55+
56+
var exception = runSyncAndReturnException<ArgumentError>(router);
57+
expect(exception.message, contains('Route entry already exists'));
58+
59+
exception = runSyncAndReturnException<ArgumentError>(router2);
60+
expect(exception.message, contains('Route entry already exists'));
61+
62+
exception = runSyncAndReturnException<ArgumentError>(router3);
4963
expect(exception.message, contains('Route entry already exists'));
5064
});
5165
});
@@ -95,17 +109,16 @@ void main() {
95109
test('contain param and wildcard together', () {
96110
final router = Spanner()
97111
..addRoute(HTTPMethod.GET, '/<lang>/item/<id>', null)
98-
..addRoute(HTTPMethod.GET, '/<lang>/item/*', null);
112+
..addRoute(HTTPMethod.GET, '/<lang>/item/*', #wild);
99113

100114
expect(
101115
router.lookup(HTTPMethod.GET, '/fr/item/12345'),
102116
havingParameters({'lang': 'fr', 'id': '12345'}),
103117
);
104118

105-
expect(
106-
router.lookup(HTTPMethod.GET, '/fr/item/12345/edit'),
107-
havingParameters({'lang': 'fr', '*': '12345/edit'}),
108-
);
119+
final result = router.lookup(HTTPMethod.GET, '/fr/item/12345/edit');
120+
expect(result?.values, [#wild]);
121+
expect(result?.params, {'lang': 'fr', 'id': '12345'});
109122
});
110123
});
111124
}

0 commit comments

Comments
 (0)