LCOV - code coverage report
Current view: top level - collections - range.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 100.0 % 30 30
Test Date: 2026-08-24 04:50:03 Functions: - 0 0
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright 2026 The Authors. See the AUTHORS file for details.
       2              : //
       3              : // Licensed under the Apache License, Version 2.0 (the "License");
       4              : // you may not use this file except in compliance with the License.
       5              : // You may obtain a copy of the License at
       6              : //
       7              : //      https://www.apache.org/licenses/LICENSE-2.0
       8              : //
       9              : // Unless required by applicable law or agreed to in writing, software
      10              : // distributed under the License is distributed on an "AS IS" BASIS,
      11              : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12              : // See the License for the specific language governing permissions and
      13              : // limitations under the License.
      14              : 
      15              : import 'mapper.dart' show MappedObject;
      16              : 
      17              : /// Handy function for grabbing a [Range] and iterating over it.
      18              : ///
      19              : /// ```dart
      20              : /// for (var i in range(stop: 10)) {
      21              : ///   print(i);
      22              : /// }
      23              : /// ```
      24            1 : Iterable<num> range({
      25              :   num start = 0,
      26              :   required num stop,
      27              :   num step = 1,
      28              :   bool stopExclusive = true,
      29              : }) {
      30            1 :   return Range(
      31              :     start: start,
      32              :     stop: stop,
      33              :     step: step,
      34              :     stopExclusive: stopExclusive,
      35            1 :   ).generate();
      36              : }
      37              : 
      38              : /// A numerical progression.
      39              : ///
      40              : /// For basic usage, just provide the (exclusive) [stop] value - note that,
      41              : /// by default, the range is _exclusive_ of the stop value:
      42              : ///
      43              : /// ```dart
      44              : /// final range = Range(stop:5);
      45              : /// print(range.toList());
      46              : /// ```
      47              : ///
      48              : /// Result: `[0, 1, 2, 3, 4]`
      49              : ///
      50              : /// Providing a [start] value allows for a non-zero starting point:
      51              : ///
      52              : /// ```dart
      53              : /// final range = Range(start: 1, stop:5);
      54              : /// print(range.toList());
      55              : /// ```
      56              : ///
      57              : /// Result: `[1, 2, 3, 4]`
      58              : ///
      59              : /// Consider using the [range()] function if you just want to use a one-off
      60              : /// range in a loop.
      61              : class Range implements MappedObject {
      62              :   /// The starting value of the range (inclusive), defaults to 0
      63              :   final num start;
      64              : 
      65              :   /// The end value of the range (exclusive)
      66              :   final num stop;
      67              : 
      68              :   /// The step progression, defaults to 1. Must be greater than 0
      69              :   final num step;
      70              : 
      71              :   /// If false, the [stop] value is inclusive. Defaults to true
      72              :   final bool stopExclusive;
      73              : 
      74              :   /// Range constructor
      75              :   ///
      76              :   /// Create a range that begins at [start]. By default, [start] is 0.
      77              :   ///
      78              :   /// Ordinarily, the Range will end at [stop], exclusive of [stop].
      79              :   /// If [stopExclusive] is false, the value of [stop] is included in the range.
      80              :   ///
      81              :   /// The [step] determines the amount by which the range progresses on each
      82              :   /// iteration. By default [step] is 1.
      83              :   ///
      84              :   /// ```dart
      85              :   /// final r = range(stop: 10, step: 2);
      86              :   /// print(r.toList());
      87              :   /// ```
      88              :   ///
      89              :   /// Result: `[0, 2, 4, 6, 8]`
      90              :   ///
      91              :   /// [step] is always a positive number - an [ArgumentError] is thrown
      92              :   /// if [step] is <= 0.
      93              :   ///
      94              :   /// If the range is moving backwards (e.g. 10 -> 0), [Range] will correctly
      95              :   /// step in the reverse direction.
      96              :   ///
      97              :   /// For example, [start] at 10 and [stop] before 0, with a [step] of 2:
      98              :   ///
      99              :   /// ```dart
     100              :   /// final r = range(start: 10, stop: 0, step: 2);
     101              :   /// print(r.toList());
     102              :   /// ```
     103              :   ///
     104              :   /// Result: `[10, 8, 6, 4, 2]`
     105              :   ///
     106              :   /// To get to 0, set [stopExclusive] to `false`:
     107              :   ///
     108              :   /// ```dart
     109              :   /// final r = range(start: 10, stop: 0, step: 2, stopExclusive: false);
     110              :   /// print(r.toList());
     111              :   /// ```
     112              :   ///
     113              :   /// Result: `[10, 8, 6, 4, 2, 0]`
     114            1 :   Range({
     115              :     this.start = 0,
     116              :     required this.stop,
     117              :     this.step = 1,
     118              :     this.stopExclusive = true,
     119              :   }) {
     120            4 :     if (step <= 0) throw ArgumentError.value(step, 'step');
     121              :   }
     122              : 
     123              :   /// Generate all list of all values in the range.
     124              :   ///
     125              :   /// Note that this method creates a new list each call.
     126              :   /// Consider maintaining a copy of the result in your own code
     127              :   /// rather than calling [toList] multiple times.
     128              :   ///
     129              :   /// _Why not cache the result in the object?_
     130              :   /// The range could be quite large.
     131            1 :   List<num> toList() {
     132            1 :     final result = <num>[];
     133              : 
     134            2 :     for (var i in generate()) {
     135            1 :       result.add(i);
     136              :     }
     137              : 
     138              :     return result;
     139              :   }
     140              : 
     141              :   /// Generate the values for the range.
     142              :   ///
     143              :   /// Handy with `for` loops.
     144              :   ///
     145              :   /// Example:
     146              :   ///
     147              :   /// ```dart
     148              :   /// final list = <int>[];
     149              :   /// final range = Range(stop: 10);
     150              :   /// for (var i in range.generate()) {
     151              :   ///   list.add(i);
     152              :   /// }
     153              :   /// print(list);
     154              :   /// ```
     155              :   ///
     156              :   /// Result: `[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`
     157            1 :   Iterable<num> generate() sync* {
     158            3 :     if (start <= stop) {
     159            5 :       final lastValue = stopExclusive ? stop : stop + step;
     160            4 :       for (num i = start; i < lastValue; i += step) {
     161              :         yield i;
     162              :       }
     163              :     } else {
     164            5 :       final lastValue = stopExclusive ? stop : stop - step;
     165            4 :       for (num i = start; i > lastValue; i -= step) {
     166              :         yield i;
     167              :       }
     168              :     }
     169              :   }
     170              : 
     171              :   /// True if [input] is between [start] (inclusive) and [stop].
     172              :   ///
     173              :   /// If [stopExclusive] is false, [stop] is included in the range,
     174              :   /// otherwise it is not.
     175              :   ///
     176              :   /// Note that [step] is considered in the evaluation - the [input]
     177              :   /// must be an increment of the range.
     178              :   ///
     179              :   /// As this method calls [toList], it can be expensive and you
     180              :   /// should consider caching the result of [toList] and calling
     181              :   /// that list's `contains` method.
     182            3 :   bool contains(num input) => toList().contains(input);
     183              : 
     184            1 :   @override
     185              :   bool operator ==(Object other) {
     186            1 :     if (other is Range) {
     187            3 :       return other.start == start &&
     188            3 :           other.stop == stop &&
     189            3 :           other.step == step &&
     190            3 :           other.stopExclusive == stopExclusive;
     191              :     }
     192              :     return false;
     193              :   }
     194              : 
     195            1 :   @override
     196            5 :   int get hashCode => Object.hash(start, stop, step, stopExclusive);
     197              : 
     198            1 :   @override
     199            1 :   Map<String, dynamic> toMap() => {
     200            1 :     'start': start,
     201            1 :     'stop': stop,
     202            1 :     'step': step,
     203            1 :     'stopExclusive': stopExclusive,
     204              :   };
     205              : }
        

Generated by: LCOV version 2.0-1