-
I've written a parser and it works just fine. But wanted to get feedback to see if I'm following best practices. A few things I feel a bit iffy about:
import 'package:petitparser/petitparser.dart';
import 'any_string.dart';
Parser<Duration> _buildDurationParser() {
final hourUnit = anyStringIgnoringCase(['hours', 'hour', 'hrs', 'hr', 'h']);
final minuteUnit =
anyStringIgnoringCase(['minutes', 'minute', 'mins', 'min', 'm']);
final numberWithDecimal =
(char('0').or(digit().plus()) & char('.').seq(digit().plus())).flatten();
final wholeNumber = (char('0').or(digit().plus())).flatten();
final justDecimal = char('.').seq(digit().plus()).flatten();
final number = [numberWithDecimal, wholeNumber, justDecimal]
.toChoiceParser()
.map((value) => double.parse(value as String));
final hours =
number.seq(whitespace().star()).seq(hourUnit).map<Duration>((value) {
final hours = value[0] as double;
final seconds = hours * Duration.secondsPerHour;
return Duration(seconds: seconds.toInt());
});
final minutes =
number.seq(whitespace().star()).seq(minuteUnit).map<Duration>((value) {
final minutes = value[0] as double;
final seconds = minutes * Duration.secondsPerMinute;
return Duration(seconds: seconds.toInt());
});
final hoursAndMinutes =
hours.seq(whitespace().star()).seq(minutes).map<Duration>((value) {
final hours = value[0] as Duration;
final minutes = value[2] as Duration;
return hours + minutes;
});
return [hoursAndMinutes, hours, minutes].toChoiceParser().cast<Duration>();
}
final durationParser = _buildDurationParser();
Duration parseDuration(String durationString) {
final result = durationParser.parse(durationString);
return result.value;
}
bool isDurationValid(String durationString) {
return durationParser.accept(durationString);
} And my unit tests below: import "package:test/test.dart";
import 'duration.dart';
void main() {
group('parseDuration', () {
test('basic hours', () {
expect(parseDuration('0h'), Duration.zero);
expect(parseDuration('1h'), Duration(hours: 1));
expect(parseDuration('1 h'), Duration(hours: 1));
expect(parseDuration('1 hour'), Duration(hours: 1));
expect(parseDuration('1.5 hours'), Duration(hours: 1, minutes: 30));
expect(parseDuration('.25 h'), Duration(hours: 0, minutes: 15));
});
test('basic minutes', () {
expect(parseDuration('5 min'), Duration(minutes: 5));
expect(parseDuration('30min'), Duration(minutes: 30));
expect(parseDuration('90m'), Duration(hours: 1, minutes: 30));
});
test('hours and minutes', () {
expect(parseDuration('5h30min'), Duration(hours: 5, minutes: 30));
expect(
parseDuration('5 hours 30 minutes'), Duration(hours: 5, minutes: 30));
});
});
} Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
|
Beta Was this translation helpful? Give feedback.
-
Makes sense. I upgraded to the null safety version and got some "unnecessary cast" links so confirming it's fixed on
Didn't realize final vars are initialized on first read. So my
Got it thanks.
Makes sense. The preference is because the operators are easier to read right? I like how the operators read too, but looks like I need explicit casts with them. For example: final amPm = (am | pm).cast<MapEntry<_DTField, int>>();
// vs
final amPm = [am, pm].toChoiceParser(); Am I right to assume this is a current limitation of Dart's type inference?
Good point. |
Beta Was this translation helpful? Give feedback.
There was a bug with the return type of the
toChoiceParser
, this has been fixed in the null-safety branch for a while: b5e85d6. Could probably be cherrypicked into older versions, but since this is potentially breaking existing code I am reluctant to release a new version.Not sure what you mean by petitparser lazily compiles itself? I don't see anything going on lazily inside your
_buildX
function, though there is nothing particularly expensive either and it is only called once when the final variable is read first. Dart initializes final top-level variables lazily on the first read.Casting things in the
map
function shouldn't be necessary. If your function returnsR
the returned …