From 25ac644d54c0b77cc9952eca038945bccde07d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Ma=C3=B1osa?= Date: Sun, 10 Mar 2024 18:26:58 +0100 Subject: [PATCH] =?UTF-8?q?refactor(size):=20=E2=99=BB=EF=B8=8F=20split=20?= =?UTF-8?q?`PerfectSize`=20and=20`ImperfectSize`=20sub-extension=20types?= =?UTF-8?q?=20(#437)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(size): ♻️ split `PerfectSize` and `ImperfectSize` sub-extension types * refactor(size): ♻️ simplify exception handling for invalid `Interval`s * docs(size): 📖 specify subtype in static const docs --- lib/src/interval/size.dart | 174 +++++++++++++++++++------------ test/src/interval/size_test.dart | 15 --- 2 files changed, 107 insertions(+), 82 deletions(-) diff --git a/lib/src/interval/size.dart b/lib/src/interval/size.dart index addc5654..99ad2514 100644 --- a/lib/src/interval/size.dart +++ b/lib/src/interval/size.dart @@ -8,48 +8,48 @@ import 'quality.dart'; /// An [Interval] size. @immutable -extension type const Size._(int value) implements int { - /// Creates a new [Size] from [value]. - const Size(this.value) : assert(value != 0, 'Value must be non-zero.'); +extension type const Size._(int size) implements int { + /// Creates a new [Size] from [size]. + const Size(this.size) : assert(size != 0, 'Value must be non-zero.'); - /// A unison [Size]. - static const unison = Size(1); + /// A unison [PerfectSize]. + static const unison = PerfectSize(1); - /// A second [Size]. - static const second = Size(2); + /// A second [ImperfectSize]. + static const second = ImperfectSize(2); - /// A third [Size]. - static const third = Size(3); + /// A third [ImperfectSize]. + static const third = ImperfectSize(3); - /// A fourth [Size]. - static const fourth = Size(4); + /// A fourth [PerfectSize]. + static const fourth = PerfectSize(4); - /// A fifth [Size]. - static const fifth = Size(5); + /// A fifth [PerfectSize]. + static const fifth = PerfectSize(5); - /// A sixth [Size]. - static const sixth = Size(6); + /// A sixth [ImperfectSize]. + static const sixth = ImperfectSize(6); - /// A seventh [Size]. - static const seventh = Size(7); + /// A seventh [ImperfectSize]. + static const seventh = ImperfectSize(7); - /// An octave [Size]. - static const octave = Size(8); + /// An octave [PerfectSize]. + static const octave = PerfectSize(8); - /// A ninth [Size]. - static const ninth = Size(9); + /// A ninth [ImperfectSize]. + static const ninth = ImperfectSize(9); - /// A tenth [Size]. - static const tenth = Size(10); + /// A tenth [ImperfectSize]. + static const tenth = ImperfectSize(10); - /// An eleventh [Size]. - static const eleventh = Size(11); + /// An eleventh [PerfectSize]. + static const eleventh = PerfectSize(11); - /// A twelfth [Size]. - static const twelfth = Size(12); + /// A twelfth [PerfectSize]. + static const twelfth = PerfectSize(12); - /// A thirteenth [Size]. - static const thirteenth = Size(13); + /// A thirteenth [ImperfectSize]. + static const thirteenth = ImperfectSize(13); /// [Size] to the corresponding [ImperfectQuality.minor] or /// [PerfectQuality.perfect] semitones. @@ -122,42 +122,6 @@ extension type const Size._(int value) implements int { return sizeAbs + sizeAbs ~/ Size.octave; } - /// The [PerfectQuality.perfect] interval from this [Size]. - /// - /// Example: - /// ```dart - /// Size.unison.perfect == Interval.P1 - /// Size.fourth.perfect == Interval.P4 - /// (-Size.fifth).perfect == -Interval.P5 - /// ``` - Interval get perfect => isPerfect - ? Interval.perfect(this) - : (throw ArgumentError.value(this, 'size', 'Invalid perfect size')); - - /// The [ImperfectQuality.major] interval from this [Size]. - /// - /// Example: - /// ```dart - /// Size.second.major == Interval.M2 - /// Size.sixth.major == Interval.M6 - /// (-Size.ninth).major == -Interval.M9 - /// ``` - Interval get major => isPerfect - ? (throw ArgumentError.value(this, 'size', 'Invalid imperfect size')) - : Interval.imperfect(this, ImperfectQuality.major); - - /// The [ImperfectQuality.minor] interval from this [Size]. - /// - /// Example: - /// ```dart - /// Size.third.minor == Interval.m3 - /// Size.seventh.minor == Interval.m7 - /// (-Size.sixth).minor == -Interval.m6 - /// ``` - Interval get minor => isPerfect - ? (throw ArgumentError.value(this, 'size', 'Invalid imperfect size')) - : Interval.imperfect(this, ImperfectQuality.minor); - /// The [PerfectQuality.diminished] or [ImperfectQuality.diminished] interval /// from this [Size]. /// @@ -217,7 +181,7 @@ extension type const Size._(int value) implements int { /// const Size(-22).simple == -Size.octave /// ``` Size get simple => Size( - isCompound ? absShift.nonZeroMod(Size.octave) * sign : value, + isCompound ? absShift.nonZeroMod(Size.octave) * sign : size, ); /// This [Size] formatted as a string. @@ -232,5 +196,81 @@ extension type const Size._(int value) implements int { /// -const Size(-7) == Size.seventh /// ``` @redeclare - Size operator -() => Size(-value); + Size operator -() => Size(-size); +} + +/// An [Interval.perfect] size. +extension type const PerfectSize._(int size) implements Size { + /// Creates a new [PerfectSize] from [size]. + const PerfectSize(this.size) + // Copied from [Size.isPerfect] to allow const. + : assert( + ((size < 0 ? 0 - size : size) + (size < 0 ? 0 - size : size) ~/ 8) % + 4 < + 2, + 'Interval must be perfect.', + ); + + /// The [PerfectQuality.perfect] interval from this [PerfectSize]. + /// + /// Example: + /// ```dart + /// Size.unison.perfect == Interval.P1 + /// Size.fourth.perfect == Interval.P4 + /// (-Size.fifth).perfect == -Interval.P5 + /// ``` + Interval get perfect => Interval.perfect(this); + + /// The negation of this [PerfectSize]. + /// + /// Example: + /// ```dart + /// -Size.fifth == const Size(-5) + /// -const Size(-8) == Size.octave + /// ``` + @redeclare + PerfectSize operator -() => PerfectSize(-size); +} + +/// An [Interval.imperfect] size. +extension type const ImperfectSize._(int size) implements Size { + /// Creates a new [ImperfectSize] from [size]. + const ImperfectSize(this.size) + // Copied from [Size.isPerfect] to allow const. + : assert( + ((size < 0 ? 0 - size : size) + (size < 0 ? 0 - size : size) ~/ 8) % + 4 >= + 2, + 'Interval must be imperfect.', + ); + + /// The [ImperfectQuality.major] interval from this [ImperfectSize]. + /// + /// Example: + /// ```dart + /// Size.second.major == Interval.M2 + /// Size.sixth.major == Interval.M6 + /// (-Size.ninth).major == -Interval.M9 + /// ``` + Interval get major => Interval.imperfect(this, ImperfectQuality.major); + + /// The [ImperfectQuality.minor] interval from this [ImperfectSize]. + /// + /// Example: + /// ```dart + /// Size.third.minor == Interval.m3 + /// Size.seventh.minor == Interval.m7 + /// (-Size.sixth).minor == -Interval.m6 + /// ``` + Interval get minor => Interval.imperfect(this, ImperfectQuality.minor); + + /// The negation of this [ImperfectSize]. + /// + /// Example: + /// ```dart + /// -Size.third == const Size(-3) + /// -const Size(-7) == Size.seventh + /// ``` + @redeclare + ImperfectSize operator -() => ImperfectSize(-size); } diff --git a/test/src/interval/size_test.dart b/test/src/interval/size_test.dart index 346d8f43..f2fa9596 100644 --- a/test/src/interval/size_test.dart +++ b/test/src/interval/size_test.dart @@ -51,11 +51,6 @@ void main() { expect(Size.fourth.perfect, Interval.P4); expect((-Size.fifth).perfect, -Interval.P5); }); - - test('throws an ArgumentError if Size is not perfect', () { - expect(() => Size.second.perfect, throwsArgumentError); - expect(() => Size.sixth.perfect, throwsArgumentError); - }); }); group('.major', () { @@ -64,11 +59,6 @@ void main() { expect(Size.sixth.major, Interval.M6); expect((-Size.ninth).major, -Interval.M9); }); - - test('throws an ArgumentError if Size is perfect', () { - expect(() => Size.unison.major, throwsArgumentError); - expect(() => Size.fifth.major, throwsArgumentError); - }); }); group('.minor', () { @@ -77,11 +67,6 @@ void main() { expect(Size.seventh.minor, Interval.m7); expect((-Size.sixth).minor, -Interval.m6); }); - - test('throws an ArgumentError if Size is perfect', () { - expect(() => Size.octave.minor, throwsArgumentError); - expect(() => Size.fourth.minor, throwsArgumentError); - }); }); group('.diminished', () {