LCOV - code coverage report
Current view: top level - src - range.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 100.0 % 29 29
Test Date: 2026-06-16 03:31:00 Functions: - 0 0
Legend: Lines: hit not hit

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

Generated by: LCOV version 2.0-1