• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

albertms10 / music_notes / 23773147639

30 Mar 2026 11:42PM UTC coverage: 98.558% (-1.4%) from 100.0%
23773147639

Pull #705

github

web-flow
Merge 322e71f14 into c5516e2c0
Pull Request #705: refactor!: 💥 rewrite `toString` into a more succinct `format` method

44 of 70 new or added lines in 24 files covered. (62.86%)

2 existing lines in 1 file now uncovered.

1777 of 1803 relevant lines covered (98.56%)

1.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.65
/lib/src/notation/notation_system.dart
1
// ignore_for_file: one_member_abstracts - Code reusability
2

3
/// An abstract representation of a notation system for parsing
4
/// and formatting [I].
5
///
6
/// The [parse] and [format] methods should be designed to be [inverses](https://en.wikipedia.org/wiki/Inverse_function)
7
/// of each other:
8
/// the output of [format] should be a valid argument for [parse], and
9
/// `parse(format(value))` should return a value equal to the original value.
10
abstract class NotationSystem<I, O> implements Parser<O, I>, Formatter<I, O> {
11
  /// Creates a new formatter.
12
  const NotationSystem();
1✔
13

14
  /// Parses [source] as [I].
15
  ///
16
  /// The input [source] should typically be produced by [format], ensuring
17
  /// that `parse(format(value)) == value`.
18
  ///
19
  /// If the [source] string does not contain a valid [I], a [FormatException]
20
  /// should be thrown.
21
  @override
22
  I parse(O source);
23

24
  /// Formats this [I].
25
  ///
26
  /// The output of this method should be accepted by [parse] to reconstruct
27
  /// the original value.
28
  @override
29
  O format(I value);
30
}
31

32
/// An abstract representation of a notation system for parsing
33
/// and formatting [V] from and to a string.
34
abstract class StringNotationSystem<V> extends NotationSystem<V, String>
35
    implements StringFormatter<V>, StringParser<V> {
36
  /// Creates a new formatter.
37
  const StringNotationSystem();
1✔
38

39
  @override
1✔
40
  RegExp? get regExp => null;
41

42
  @override
1✔
43
  bool matches(String source) =>
44
      regExp == null ||
1✔
45
      RegExp(
1✔
46
        '^${regExp?.pattern}\$',
3✔
47
        caseSensitive: regExp?.isCaseSensitive ?? true,
2✔
48
        unicode: regExp?.isUnicode ?? false,
2✔
49
      ).hasMatch(source);
1✔
50

51
  /// Parses [source] as [V].
52
  ///
53
  /// The input [source] should typically be produced by [format], ensuring
54
  /// that `parse(format(value)) == value`.
55
  ///
56
  /// If the [source] string does not contain a valid [V], a [FormatException]
57
  /// should be thrown.
58
  @override
1✔
59
  V parse(String source) => parseMatch(
1✔
60
    regExp?.firstMatch(source) ?? (throw FormatException('Invalid $V', source)),
4✔
61
  );
62

63
  @override
1✔
64
  V parseMatch(RegExpMatch match) => throw UnimplementedError(
1✔
65
    'parseMatch is not implemented for $runtimeType.',
2✔
66
  );
67

68
  /// Formats this [V].
69
  ///
70
  /// The output of this method should be accepted by [parse] to reconstruct
71
  /// the original value.
72
  @override
73
  String format(V value);
74
}
75

76
/// An abstract representation of a parser for [V].
77
abstract interface class Parser<I, V> {
78
  /// Parses [source] as [V].
79
  V parse(I source);
80
}
81

82
/// An abstract representation of a parser for [V].
83
abstract interface class StringParser<V> extends Parser<String, V> {
84
  /// The regular expression for matching [V].
85
  RegExp? get regExp;
86

87
  /// Whether [source] can be parsed with [parse].
88
  bool matches(String source);
89

90
  /// Parses [source] as [V].
91
  @override
92
  V parse(String source);
93

94
  /// Parses [match] from [regExp] as [V].
95
  V parseMatch(RegExpMatch match);
96
}
97

98
/// A [StringParser] chain.
99
extension StringParserChain<V> on List<StringParser<V>> {
100
  /// Parses [source] from this chain of [StringParser]s.
101
  V parse(String source) =>
1✔
102
      firstMatchingParser(source)?.parse(source) ??
2✔
103
      (throw FormatException('End of parser chain: invalid $V', source));
2✔
104

105
  /// Returns the first [StringParser] in this chain that matches [source],
106
  /// or `null` if none match.
107
  StringParser<V>? firstMatchingParser(String source) {
1✔
108
    for (final parser in this) {
2✔
109
      if (parser.matches(source)) return parser;
1✔
110
    }
111

112
    return null;
113
  }
114
}
115

116
/// An abstract representation of a formatter for [V].
117
abstract interface class Formatter<V, O> {
118
  /// Formats this [V].
119
  O format(V value);
120
}
121

122
/// An abstract representation of a string formatter for [V].
123
abstract interface class StringFormatter<V> extends Formatter<V, String> {}
124

125
/// A value that can be formatted using a [Formatter].
126
abstract class Formattable<V> {
127
  /// Creates a new [Formattable].
NEW
128
  const Formattable();
×
129

130
  /// Formats this [V].
131
  String format();
132
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc