| // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | import "dart:async" show FutureOr; | 
 | import "package:expect/expect.dart"; | 
 | import "package:expect/static_type_helper.dart"; | 
 | // All extensions are exported by `dart:core`. | 
 |  | 
 | void main() { | 
 |   // Null-related extensions. | 
 |   testNonNulls(); | 
 |   testFirstOrNull(); | 
 |   testLastOrNull(); | 
 |   testSingleOrNull(); | 
 |   testElementAtOrNull(); | 
 |  | 
 |   // Record-related extensions. | 
 |   testIndexed(); | 
 | } | 
 |  | 
 | void testNonNulls() { | 
 |   // Static behavior. | 
 |   { | 
 |     // Removes nullability from element type. | 
 |     Iterable<int?> target = []; | 
 |     var result = target.nonNulls; | 
 |     result.expectStaticType<Exactly<Iterable<int>>>(); | 
 |   } | 
 |   { | 
 |     // Works on subtypes of iterable. | 
 |     List<int?> target = []; | 
 |     var result = target.nonNulls; | 
 |     result.expectStaticType<Exactly<Iterable<int>>>(); | 
 |   } | 
 |   { | 
 |     // Works on non-nullable types too (wish it didn't). | 
 |     Iterable<int> target = []; | 
 |     var result = target.nonNulls; | 
 |     result.expectStaticType<Exactly<Iterable<int>>>(); | 
 |   } | 
 |   { | 
 |     // Removes nullability from `Never?`, giving `Never`. | 
 |     // (Cannot remove nullability from `Null`, so doesn't match | 
 |     // `Iterable<Null>`.) | 
 |     Iterable<Never?> target = []; | 
 |     var result = target.nonNulls; | 
 |     result.expectStaticType<Exactly<Iterable<Never>>>(); | 
 |   } | 
 |  | 
 |   // Dynamic behavior. | 
 |  | 
 |   void test<T extends Object>( | 
 |     String name, | 
 |     Iterable<T?> input, | 
 |     List<T> expectedResults, | 
 |   ) { | 
 |     var actualResults = input.nonNulls; | 
 |     Expect.type<Iterable<T>>(actualResults, "$name type"); | 
 |     Expect.listEquals(expectedResults, [...actualResults], "$name result"); | 
 |   } | 
 |  | 
 |   test<int>("empty iterable", Iterable.empty(), []); | 
 |   test<int>("empty list", [], []); | 
 |   test<int>("all non-null", numbers(5), [1, 2, 3, 4, 5]); | 
 |   test<int>("all null", numbers(5, where: none), []); | 
 |   test<int>("one non-null", numbers(5, where: only(3)), [3]); | 
 |   test<int>("some null", numbers(5, where: even), [2, 4]); | 
 |   test<int>("some null, list", [null, 2, null, 4, null], [2, 4]); | 
 |  | 
 |   // Is lazy. | 
 |   var nonNulls = numbers(5, where: even, throwAt: 3).nonNulls; | 
 |   var it = nonNulls.iterator; | 
 |   Expect.isTrue(it.moveNext()); | 
 |   Expect.equals(2, it.current); | 
 |   Expect.throws<UnimplementedError>(it.moveNext); | 
 | } | 
 |  | 
 | void testFirstOrNull() { | 
 |   // Static behavior. | 
 |   // (Tested to ensure extension captures the correct type, | 
 |   // and the correct extension member is applied). | 
 |   { | 
 |     Iterable<int> target = []; | 
 |     var result = target.firstOrNull; | 
 |     result.expectStaticType<Exactly<int?>>(); | 
 |   } | 
 |   { | 
 |     Iterable<int?> target = []; | 
 |     var result = target.firstOrNull; | 
 |     result.expectStaticType<Exactly<int?>>(); | 
 |   } | 
 |   { | 
 |     Iterable<Never> target = []; | 
 |     var result = target.firstOrNull; | 
 |     result.expectStaticType<Exactly<Null>>(); | 
 |   } | 
 |   // Dynamic behavior. | 
 |   void test<T extends Object>( | 
 |     String name, | 
 |     Iterable<T?> source, | 
 |     T? expectedResult, | 
 |   ) { | 
 |     var actualResult = source.firstOrNull; | 
 |     Expect.equals(expectedResult, actualResult, "firstOrNull $name"); | 
 |   } | 
 |  | 
 |   test<int>("Empty iterable", Iterable.empty(), null); | 
 |   test<Never>("Empty iterable", Iterable.empty(), null); | 
 |   test<int>("Empty list", [], null); | 
 |   test<int>("Single value", numbers(1), 1); | 
 |   test<int>("Multiple values", numbers(3), 1); | 
 |   test<int>("Nullable values", numbers(3, where: even), null); | 
 |   test<int>("Stops after first", numbers(3, throwAt: 2), 1); | 
 |   Expect.throws<UnimplementedError>( | 
 |     () => numbers(3, throwAt: 1).firstOrNull, | 
 |     null, | 
 |     "firstOrNull first throws", | 
 |   ); | 
 | } | 
 |  | 
 | void testLastOrNull() { | 
 |   // Static behavior. | 
 |   { | 
 |     Iterable<int> target = []; | 
 |     var result = target.lastOrNull; | 
 |     result.expectStaticType<Exactly<int?>>(); | 
 |   } | 
 |   { | 
 |     Iterable<int?> target = []; | 
 |     var result = target.lastOrNull; | 
 |     result.expectStaticType<Exactly<int?>>(); | 
 |   } | 
 |   { | 
 |     Iterable<Never> target = []; | 
 |     var result = target.lastOrNull; | 
 |     result.expectStaticType<Exactly<Null>>(); | 
 |   } | 
 |   // Dynamic behavior. | 
 |   void test<T>(String name, Iterable<T> source, T? expectedResult) { | 
 |     var actualResult = source.lastOrNull; | 
 |     Expect.equals(expectedResult, actualResult, "lastOrNull $name"); | 
 |   } | 
 |  | 
 |   test<int>("Empty iterable", Iterable.empty(), null); | 
 |   test<Never>("Empty iterable", Iterable.empty(), null); | 
 |   test<int>("Empty list", [], null); | 
 |   test<int?>("Single value", numbers(1), 1); | 
 |   test<int?>("Multiple values", numbers(3), 3); | 
 |   test<int?>("Nullable values", numbers(3, where: even), null); | 
 |   Expect.throws<UnimplementedError>( | 
 |     () => numbers(3, throwAt: 1).lastOrNull, | 
 |     null, | 
 |     "lastOrNull first throws", | 
 |   ); | 
 |   Expect.throws<UnimplementedError>( | 
 |     () => numbers(3, throwAt: 3).lastOrNull, | 
 |     null, | 
 |     "lastOrNull last throws", | 
 |   ); | 
 |   Expect.throws<UnimplementedError>( | 
 |     () => CurrentThrowIterable<int?>(numbers(3), 2).lastOrNull, | 
 |     null, | 
 |     "lastOrNull throw on current", | 
 |   ); | 
 | } | 
 |  | 
 | void testSingleOrNull() { | 
 |   // Static behavior. | 
 |   { | 
 |     Iterable<int> target = []; | 
 |     var result = target.singleOrNull; | 
 |     result.expectStaticType<Exactly<int?>>(); | 
 |   } | 
 |   { | 
 |     Iterable<int?> target = []; | 
 |     var result = target.singleOrNull; | 
 |     result.expectStaticType<Exactly<int?>>(); | 
 |   } | 
 |   { | 
 |     Iterable<Never> target = []; | 
 |     var result = target.singleOrNull; | 
 |     result.expectStaticType<Exactly<Null>>(); | 
 |   } | 
 |   // Dynamic behavior. | 
 |   void test<T>(String name, Iterable<T> source, T? expectedResult) { | 
 |     var actualResult = source.singleOrNull; | 
 |     Expect.equals(expectedResult, actualResult, "singleOrNull $name"); | 
 |   } | 
 |  | 
 |   test<int>("Empty iterable", Iterable.empty(), null); | 
 |   test<Never>("Empty iterable", Iterable.empty(), null); | 
 |   test<int>("Empty list", [], null); | 
 |   test<int?>("Single value", numbers(1), 1); | 
 |   test<int?>("Multiple values", numbers(3), null); | 
 |   test<int?>("Nullable values", numbers(3, where: even), null); | 
 |   Expect.throws<UnimplementedError>( | 
 |     () => numbers(3, throwAt: 1).singleOrNull, | 
 |     null, | 
 |     "singleOrNull first throws", | 
 |   ); | 
 |   Expect.throws<UnimplementedError>( | 
 |     () => numbers(3, throwAt: 2).singleOrNull, | 
 |     null, | 
 |     "singleOrNull second throws", | 
 |   ); | 
 |   test<int?>("Throws after two", numbers(3, throwAt: 3), null); | 
 | } | 
 |  | 
 | void testElementAtOrNull() { | 
 |   // Static behavior. | 
 |   { | 
 |     Iterable<int> target = []; | 
 |     var result = target.elementAtOrNull(0); | 
 |     result.expectStaticType<Exactly<int?>>(); | 
 |   } | 
 |   { | 
 |     Iterable<int?> target = []; | 
 |     var result = target.elementAtOrNull(0); | 
 |     result.expectStaticType<Exactly<int?>>(); | 
 |   } | 
 |   { | 
 |     Iterable<Never> target = []; | 
 |     var result = target.elementAtOrNull(0); | 
 |     result.expectStaticType<Exactly<Null>>(); | 
 |   } | 
 |   // Dynamic behavior. | 
 |   void test<T>(String name, Iterable<T> source, int index, T? expectedResult) { | 
 |     var actualResult = source.elementAtOrNull(index); | 
 |     Expect.equals( | 
 |       expectedResult, | 
 |       actualResult, | 
 |       "elementAtOrNull($index) $name", | 
 |     ); | 
 |   } | 
 |  | 
 |   Expect.throwsArgumentError( | 
 |     () => numbers(3).elementAtOrNull(-1), | 
 |     "elementAtOrNull(negative) first throws", | 
 |   ); | 
 |  | 
 |   test<int>("Empty iterable", Iterable<int>.empty(), 0, null); | 
 |   test<int>("Empty iterable", Iterable<int>.empty(), 1000000, null); | 
 |   test<Never>("Empty iterable", Iterable<Never>.empty(), 0, null); | 
 |   test<int>("Empty list", [], 0, null); | 
 |   test<int?>("Single value", numbers(1), 0, 1); | 
 |   test<int?>("Single value", numbers(1), 1, null); | 
 |   test<int?>("Multiple values first", numbers(3), 0, 1); | 
 |   test<int?>("Multiple values mid", numbers(3), 1, 2); | 
 |   test<int?>("Multiple values last", numbers(3), 2, 3); | 
 |   test<int?>("Multiple values overshoot", numbers(3), 3, null); | 
 |   test<int?>("Nullable values found", numbers(3, where: even), 2, null); | 
 |   test<int?>("Nullable values not found", numbers(3, where: even), 3, null); | 
 |   Expect.throws<UnimplementedError>( | 
 |     () => numbers(3, throwAt: 1).elementAtOrNull(1), | 
 |     null, | 
 |     "elementAtOrNull(1) first throws", | 
 |   ); | 
 |   Expect.throws<UnimplementedError>( | 
 |     () => numbers(3, throwAt: 2).elementAtOrNull(2), | 
 |     null, | 
 |     "elementAtOrNull(2) second throws", | 
 |   ); | 
 |   test<int?>("Throws after two", numbers(3, throwAt: 3), 1, 2); | 
 |  | 
 |   var currentThrow2 = CurrentThrowIterable<int?>(numbers(3), 2); | 
 |   test<int?>("Throws current middle", currentThrow2, 0, 1); | 
 |   test<int?>("Throws current middle", currentThrow2, 2, 3); | 
 |   Expect.throws<UnimplementedError>(() => currentThrow2.elementAt(1)); | 
 | } | 
 |  | 
 | void testIndexed() { | 
 |   void test<T>(String name, Iterable<T> elements) { | 
 |     var values = elements.toList(); | 
 |     var indexed = elements.indexed; | 
 |     testRec(name, values, indexed, 0, false); | 
 |   } | 
 |  | 
 |   // Non-efficient-length iterables. | 
 |   test<int?>("NELI empty", numbers(0)); | 
 |   test<int?>("NELI single", numbers(1)); | 
 |   test<int?>("NELI two", numbers(2)); | 
 |   test<int?>("NELI more", numbers(10)); | 
 |  | 
 |   // Efficient-length iterables (a list's `map` has efficient length). | 
 |   test<int?>("ELI empty", numbers(0).toList().map((x) => x)); | 
 |   test<int?>("ELI single", numbers(1).toList().map((x) => x)); | 
 |   test<int?>("ELI two", numbers(2).toList().map((x) => x)); | 
 |   test<int?>("ELI more", numbers(10).toList().map((x) => x)); | 
 | } | 
 |  | 
 | // Helper function for `testIndexed`. Top-level because dart2js crashes | 
 | // on recursive generic local functions. | 
 | // | 
 | // If [rec] is true, we're doing a recursive test on skip/take/both, | 
 | // and `start` the number of leading elements skipped. | 
 | void testRec<T>( | 
 |   String name, | 
 |   List<T> values, | 
 |   Iterable<(int, T)> indexed, | 
 |   int start, | 
 |   bool rec, | 
 | ) { | 
 |   var length = values.length; | 
 |   Expect.equals(length, indexed.length, "$values length"); | 
 |   Expect.equals(values.isEmpty, indexed.isEmpty); | 
 |   Expect.equals(values.isNotEmpty, indexed.isNotEmpty); | 
 |   Expect.listEquals([ | 
 |     for (var i = 0; i < length; i++) (start + i, values[i]), | 
 |   ], indexed.toList()); | 
 |  | 
 |   int index = 0; | 
 |   indexed.forEach((pair) { | 
 |     Expect.equals(start + index, pair.$1); | 
 |     Expect.equals(values[index], pair.$2); | 
 |     index++; | 
 |   }); | 
 |   Expect.equals(length, index); | 
 |  | 
 |   Expect.isFalse(indexed.contains(0)); | 
 |   Expect.isFalse(indexed.contains((start - 1, 0))); | 
 |   Expect.isFalse(indexed.contains((start + length, 0))); | 
 |  | 
 |   if (values.isNotEmpty) { | 
 |     Expect.isFalse(indexed.contains(values.first)); | 
 |     Expect.equals((start, values.first), indexed.first); | 
 |     Expect.equals((start + length - 1, values.last), indexed.last); | 
 |     for (var i = 0; i < length; i++) { | 
 |       Expect.equals((start + i, values[i]), indexed.elementAt(i)); | 
 |       Expect.isTrue(indexed.contains((start + i, values[i]))); | 
 |     } | 
 |     Expect.isFalse(indexed.contains((start - 1, values.first))); | 
 |     Expect.isFalse(indexed.contains((start + length, values.last))); | 
 |     if (length == 1) { | 
 |       Expect.equals((start, values.single), indexed.single); | 
 |     } else if (!rec) { | 
 |       Expect.throws<StateError>(() => indexed.single); | 
 |       // More than one element, so test skip/take. | 
 |       testRec("$name.skip(1)", values.sublist(1), indexed.skip(1), 1, true); | 
 |       testRec( | 
 |         "$name.take(l-1)", | 
 |         values.sublist(0, length - 1), | 
 |         indexed.take(length - 1), | 
 |         0, | 
 |         true, | 
 |       ); | 
 |       if (length > 2) { | 
 |         testRec( | 
 |           "$name.skip(1).take(l-2)", | 
 |           values.sublist(1, length - 1), | 
 |           indexed.skip(1).take(length - 2), | 
 |           1, | 
 |           true, | 
 |         ); | 
 |         testRec( | 
 |           "$name.take(l-1).skip(1)", | 
 |           values.sublist(1, length - 1), | 
 |           indexed.take(length - 1).skip(1), | 
 |           1, | 
 |           true, | 
 |         ); | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | /// Generates an iterable with [length] elements. | 
 | /// | 
 | /// The elements are 1, ..., [length], except that if `isValue` returns | 
 | /// `false` for a number, it's replaced by `null`. | 
 | /// If [throwAt] is provided, the iterable throws an `UnimplementedError` | 
 | /// (which shouldn't conflict with an actual error) instead of emitting | 
 | /// a value at the [throwAt] index. | 
 | Iterable<int?> numbers( | 
 |   int length, { | 
 |   bool Function(int) where = all, | 
 |   int? throwAt, | 
 | }) sync* { | 
 |   for (var i = 1; i <= length; i++) { | 
 |     if (i == throwAt) throw UnimplementedError("Error"); | 
 |     yield where(i) ? i : null; | 
 |   } | 
 | } | 
 |  | 
 | /// Iterable which throws only when accessing [current]. | 
 | /// | 
 | /// Used to test that operations that don't need the value, | 
 | /// also don't read it. | 
 | /// | 
 | /// (Could also be achieved with | 
 | /// ``` | 
 | /// _source.toList().map((x) => x == _throwAt ? throw ... : x)); | 
 | /// ``` | 
 | /// but that assumes optimization behavior that is not necessarily tested.) | 
 | class CurrentThrowIterable<T> extends Iterable<T> { | 
 |   final T _throwAt; | 
 |   final Iterable<T> _source; | 
 |   CurrentThrowIterable(this._source, this._throwAt); | 
 |   Iterator<T> get iterator => | 
 |       CurrentThrowIterator<T>(_source.iterator, _throwAt); | 
 | } | 
 |  | 
 | class CurrentThrowIterator<T> implements Iterator<T> { | 
 |   final T _throwAt; | 
 |   Iterator<T> _source; | 
 |   CurrentThrowIterator(this._source, this._throwAt); | 
 |   bool moveNext() => _source.moveNext(); | 
 |   T get current { | 
 |     var result = _source.current; | 
 |     if (result == _throwAt) throw UnimplementedError("Error"); | 
 |     return result; | 
 |   } | 
 | } | 
 |  | 
 | bool none(_) => false; | 
 | bool all(_) => true; | 
 | bool even(int n) => n.isEven; | 
 | bool Function(int) only(int n1) => (int n2) => n1 == n2; |