2017-11-27 02:21:19 +00:00
|
|
|
part of angel_route.src.router;
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
class RouteGrammar {
|
2017-11-27 02:21:19 +00:00
|
|
|
static final Parser<String> notSlash =
|
|
|
|
match(new RegExp(r'[^/]+')).value((r) => r.span.text);
|
2018-08-20 14:24:11 +00:00
|
|
|
|
|
|
|
static final Parser<RegExp> regExp = match<RegExp>(new RegExp(r'\((.+)\)'))
|
|
|
|
.value((r) => new RegExp(r.scanner.lastMatch[1]));
|
|
|
|
|
2017-11-27 02:21:19 +00:00
|
|
|
static final Parser<String> parameterName =
|
|
|
|
match(new RegExp(r':([A-Za-z0-9_]+)'))
|
|
|
|
.value((r) => r.span.text.substring(1));
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
static final Parser<ParameterSegment> parameterSegment = chain([
|
2017-11-27 02:21:19 +00:00
|
|
|
parameterName,
|
|
|
|
match('?').value((r) => true).opt(),
|
|
|
|
regExp.opt(),
|
|
|
|
]).map((r) {
|
2017-11-28 21:07:14 +00:00
|
|
|
var s = new ParameterSegment(r.value[0], r.value[2]);
|
|
|
|
return r.value[1] == true ? new OptionalSegment(s) : s;
|
2017-11-27 02:21:19 +00:00
|
|
|
});
|
|
|
|
|
2018-08-20 14:24:11 +00:00
|
|
|
static final Parser<ParsedParameterSegment> parsedParameterSegment = chain([
|
|
|
|
match(new RegExp(r'(int|num|double)'),
|
|
|
|
errorMessage: 'Expected "int","double", or "num".')
|
|
|
|
.map((r) => r.span.text),
|
|
|
|
parameterSegment,
|
|
|
|
]).map((r) {
|
|
|
|
return new ParsedParameterSegment(r.value[0], r.value[1]);
|
|
|
|
});
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
static final Parser<WildcardSegment> wildcardSegment =
|
|
|
|
match('*').value((r) => new WildcardSegment());
|
2017-11-27 02:21:19 +00:00
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
static final Parser<ConstantSegment> constantSegment =
|
|
|
|
notSlash.map((r) => new ConstantSegment(r.value));
|
2017-11-27 02:21:19 +00:00
|
|
|
|
2018-08-20 14:24:11 +00:00
|
|
|
static final Parser<RouteSegment> routeSegment = any([
|
|
|
|
parsedParameterSegment,
|
|
|
|
parameterSegment,
|
|
|
|
wildcardSegment,
|
|
|
|
constantSegment
|
|
|
|
]);
|
2017-11-27 02:21:19 +00:00
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
static final Parser<RouteDefinition> routeDefinition = routeSegment
|
2017-11-27 02:21:19 +00:00
|
|
|
.separatedBy(match('/'))
|
2017-11-28 21:07:14 +00:00
|
|
|
.map((r) => new RouteDefinition(r.value ?? []))
|
2017-11-27 02:21:19 +00:00
|
|
|
.surroundedBy(match('/').star().opt());
|
|
|
|
}
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
class RouteDefinition {
|
|
|
|
final List<RouteSegment> segments;
|
2017-11-27 02:21:19 +00:00
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
RouteDefinition(this.segments);
|
2017-11-27 02:21:19 +00:00
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> compile() {
|
|
|
|
Parser<Map<String, dynamic>> out;
|
2017-11-27 02:21:19 +00:00
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
for (int i = 0; i < segments.length; i++) {
|
|
|
|
var s = segments[i];
|
|
|
|
bool isLast = i == segments.length - 1;
|
2017-11-27 02:21:19 +00:00
|
|
|
if (out == null)
|
2017-11-28 21:07:14 +00:00
|
|
|
out = s.compile(isLast);
|
2017-11-27 02:21:19 +00:00
|
|
|
else
|
2017-11-28 21:07:14 +00:00
|
|
|
out = s.compileNext(out.then(match('/')).index(0), isLast);
|
2017-11-27 02:21:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
abstract class RouteSegment {
|
|
|
|
Parser<Map<String, dynamic>> compile(bool isLast);
|
2018-08-20 14:24:11 +00:00
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> compileNext(
|
|
|
|
Parser<Map<String, dynamic>> p, bool isLast);
|
2017-11-27 02:21:19 +00:00
|
|
|
}
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
class ConstantSegment extends RouteSegment {
|
2017-11-27 02:21:19 +00:00
|
|
|
final String text;
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
ConstantSegment(this.text);
|
2017-11-27 02:21:19 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
String toString() {
|
|
|
|
return 'Constant: $text';
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> compile(bool isLast) {
|
2017-11-27 02:21:19 +00:00
|
|
|
return match(text).value((r) => {});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> compileNext(
|
|
|
|
Parser<Map<String, dynamic>> p, bool isLast) {
|
|
|
|
return p.then(compile(isLast)).index(0);
|
2017-11-27 02:21:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
class WildcardSegment extends RouteSegment {
|
2017-11-27 02:21:19 +00:00
|
|
|
@override
|
|
|
|
String toString() {
|
|
|
|
return 'Wildcard segment';
|
|
|
|
}
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> _compile(bool isLast) {
|
|
|
|
if (isLast) return match(new RegExp(r'.*'));
|
2017-11-27 02:21:19 +00:00
|
|
|
return match(new RegExp(r'[^/]*'));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> compile(bool isLast) {
|
|
|
|
return _compile(isLast).map((r) => {});
|
2017-11-27 02:21:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> compileNext(
|
|
|
|
Parser<Map<String, dynamic>> p, bool isLast) {
|
|
|
|
return p.then(_compile(isLast)).index(0);
|
2017-11-27 02:21:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
class OptionalSegment extends ParameterSegment {
|
|
|
|
final ParameterSegment parameter;
|
2017-11-27 02:21:19 +00:00
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
OptionalSegment(this.parameter) : super(parameter.name, parameter.regExp);
|
2017-11-27 02:21:19 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
String toString() {
|
|
|
|
return 'Optional: $parameter';
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> compile(bool isLast) {
|
|
|
|
return super.compile(isLast).opt();
|
2017-11-27 02:21:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> compileNext(
|
|
|
|
Parser<Map<String, dynamic>> p, bool isLast) {
|
2017-11-27 02:21:19 +00:00
|
|
|
return p.then(_compile().opt()).map((r) {
|
|
|
|
if (r.value[1] == null) return r.value[0];
|
|
|
|
return r.value[0]..addAll({name: Uri.decodeComponent(r.value[1])});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
class ParameterSegment extends RouteSegment {
|
2017-11-27 02:21:19 +00:00
|
|
|
final String name;
|
|
|
|
final RegExp regExp;
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
ParameterSegment(this.name, this.regExp);
|
2017-11-27 02:21:19 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
String toString() {
|
|
|
|
if (regExp != null) return 'Param: $name (${regExp.pattern})';
|
|
|
|
return 'Param: $name';
|
|
|
|
}
|
|
|
|
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> _compile() {
|
2017-11-27 02:21:19 +00:00
|
|
|
return regExp != null
|
|
|
|
? match(regExp).value((r) => r.span.text)
|
2017-11-28 21:07:14 +00:00
|
|
|
: RouteGrammar.notSlash;
|
2017-11-27 02:21:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> compile(bool isLast) {
|
2017-11-27 02:21:19 +00:00
|
|
|
return _compile().map((r) => {name: Uri.decodeComponent(r.span.text)});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2017-11-28 21:07:14 +00:00
|
|
|
Parser<Map<String, dynamic>> compileNext(
|
|
|
|
Parser<Map<String, dynamic>> p, bool isLast) {
|
2017-11-27 02:21:19 +00:00
|
|
|
return p.then(_compile()).map((r) {
|
|
|
|
return r.value[0]..addAll({name: Uri.decodeComponent(r.value[1])});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2018-08-20 14:24:11 +00:00
|
|
|
|
|
|
|
class ParsedParameterSegment extends RouteSegment {
|
|
|
|
final String type;
|
|
|
|
final ParameterSegment parameter;
|
|
|
|
|
|
|
|
ParsedParameterSegment(this.type, this.parameter);
|
|
|
|
|
|
|
|
num getValue(String s) {
|
|
|
|
switch (type) {
|
|
|
|
case 'int':
|
|
|
|
return int.parse(s);
|
|
|
|
case 'double':
|
|
|
|
return double.parse(s);
|
|
|
|
default:
|
|
|
|
return num.parse(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Parser<Map<String, dynamic>> compile(bool isLast) {
|
|
|
|
return parameter._compile().map(
|
|
|
|
(r) => {parameter.name: getValue(Uri.decodeComponent(r.span.text))});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Parser<Map<String, dynamic>> compileNext(
|
|
|
|
Parser<Map<String, dynamic>> p, bool isLast) {
|
|
|
|
return p.then(parameter._compile()).map((r) {
|
|
|
|
return r.value[0]
|
|
|
|
..addAll({parameter.name: getValue(Uri.decodeComponent(r.value[1]))});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|