LCOV - code coverage report
Current view: top level - src - gg_value_base.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 66 66 100.0 %
Date: 2021-03-09 08:35:43 Functions: 0 0 -

          Line data    Source code
       1             : // @license
       2             : // Copyright (c) 2019 - 2021 Dr. Gabriel Gatzsche. All Rights Reserved.
       3             : //
       4             : // Use of this source code is governed by terms that can be
       5             : // found in the LICENSE file in the root of this repository.
       6             : 
       7             : import 'dart:async';
       8             : 
       9             : /// Represents a value of Type T in the memory.
      10             : class GgValue<T> {
      11             :   // ...........................................................................
      12             :   /// - [seed] The initial seed of the value.
      13             :   /// - If [spam] is true, each change of the value will be added to the
      14             :   ///   stream.
      15             :   /// - If [spam] is false, updates of the value are scheduled as micro
      16             :   ///   tasks. New updates are not added until the last update has been delivered.
      17             :   ///   Only the last set value will be delivered.
      18             :   /// - [transform] allows you to keep value in a given range or transform it.
      19             :   /// - [parse] is needed when [T] is not [String], [int], [double] or [bool].
      20             :   ///   It converts a string into [T].
      21             :   /// - [toString] is needed when [T] is not [String], [int], [double] or [bool].
      22             :   ///   It converts the value into a [String].
      23           1 :   GgValue({
      24             :     required T seed,
      25             :     this.spam = false,
      26             :     this.compare,
      27             :     this.transform,
      28             :     T Function(String)? parse,
      29             :     String Function(T)? toString,
      30             :   })  : _value = seed,
      31             :         _parse = parse,
      32             :         _toString = toString {
      33           1 :     _initController();
      34           1 :     _checkType();
      35             :   }
      36             : 
      37             :   // ...........................................................................
      38             :   /// Sets the value and triggers an update on the stream.
      39           1 :   set value(T value) {
      40           2 :     if (value == _value) {
      41             :       return;
      42             :     }
      43             : 
      44           4 :     if (compare != null && compare!(value, _value)) {
      45             :       return;
      46             :     }
      47             : 
      48           4 :     _value = transform == null ? value : transform!(value);
      49             : 
      50           1 :     if (spam) {
      51           3 :       _controller.add(_value);
      52           1 :     } else if (!_isAlreadyTriggered) {
      53           1 :       _isAlreadyTriggered = true;
      54           2 :       scheduleMicrotask(() {
      55           1 :         _isAlreadyTriggered = false;
      56           2 :         if (_controller.hasListener) {
      57           3 :           _controller.add(_value);
      58             :         }
      59             :       });
      60             :     }
      61             :   }
      62             : 
      63             :   // ...........................................................................
      64             :   /// Parses [str] and writes the result into value.
      65           1 :   set stringValue(String str) {
      66           1 :     if (_parse != null) {
      67           3 :       value = _parse!.call(str);
      68           1 :     } else if (T == int) {
      69           2 :       value = int.parse(str) as T;
      70           1 :     } else if (T == double) {
      71           2 :       value = double.parse(str) as T;
      72           1 :     } else if (T == bool) {
      73           1 :       switch (str.toLowerCase()) {
      74           1 :         case 'false':
      75           1 :         case '0':
      76           1 :         case 'no':
      77           1 :           value = false as T;
      78             :           break;
      79           1 :         case 'true':
      80           1 :         case '1':
      81           1 :         case 'yes':
      82           1 :           value = true as T;
      83             :       }
      84             :     } else {
      85           1 :       value = str as T;
      86             :     }
      87             :   }
      88             : 
      89             :   // ...........................................................................
      90             :   /// Returns the [value] as [String].
      91           1 :   String get stringValue {
      92           1 :     if (_toString != null) {
      93           3 :       return _toString!.call(_value);
      94           1 :     } else if (T == String) {
      95           1 :       return _value as String;
      96           1 :     } else if (T == bool) {
      97           1 :       return (_value as bool) ? 'true' : 'false';
      98             :     } else {
      99           2 :       return _value.toString();
     100             :     }
     101             :   }
     102             : 
     103             :   // ...........................................................................
     104             :   /// Allows reducing the number of updates delivered when the value is changed
     105             :   /// multiple times.
     106             :   ///
     107             :   /// - If [spam] is true, each change of the value will be added to the stream.
     108             :   /// - If [spam] is false, updates of the value are scheduled as micro tasks.
     109             :   /// New updates are not added until the last update has been delivered.
     110             :   /// Only the last set value will be delivered.
     111             :   bool spam;
     112             : 
     113             :   // ...........................................................................
     114             :   /// If T is not a String or num, this function is used to parse a string
     115             : 
     116             :   // ...........................................................................
     117             :   /// Returns the value
     118           2 :   T get value => _value;
     119             : 
     120             :   /// Returns a stream informing about changes on the value
     121           3 :   Stream<T> get stream => _controller.stream;
     122             : 
     123             :   // ...........................................................................
     124             :   /// Call this method when the value is about to be released.
     125           1 :   void dispose() {
     126           5 :     _dispose.reversed.forEach((e) => e());
     127             :   }
     128             : 
     129             :   // ...........................................................................
     130             :   /// Is used to check if the value assigned is valid.
     131             :   final T Function(T)? transform;
     132             : 
     133             :   // ...........................................................................
     134             :   /// This operator compares to GgValue objects based on the value. When given,
     135             :   //the [compare] function is used to make the comparison.
     136           1 :   @override
     137             :   bool operator ==(Object other) =>
     138             :       identical(this, other) ||
     139           1 :       other is GgValue<T> &&
     140           1 :           ((compare != null && compare!(_value, other._value)) ||
     141           3 :               _value == other._value);
     142             : 
     143             :   // ...........................................................................
     144             :   /// The hashcode of a GgValue is calculated based on the value.
     145           1 :   @override
     146           2 :   int get hashCode => _value.hashCode;
     147             : 
     148             :   // ######################
     149             :   // Private
     150             :   // ######################
     151             : 
     152             :   final List<Function()> _dispose = [];
     153             : 
     154             :   // ...........................................................................
     155           1 :   void _checkType() {
     156           1 :     _checkParseMethodNeeded();
     157           1 :     _checkToStringMethodNeeded();
     158             :   }
     159             : 
     160             :   // ...........................................................................
     161           1 :   void _checkParseMethodNeeded() {
     162           4 :     if (T != String && T != double && T != int && T != bool) {
     163           1 :       if (_parse == null) {
     164           1 :         throw ArgumentError(
     165           2 :             'Missing "parse" method for unknown type "${T.toString()}".');
     166             :       }
     167             :     }
     168             :   }
     169             : 
     170             :   // ...........................................................................
     171           1 :   void _checkToStringMethodNeeded() {
     172           4 :     if (T != String && T != double && T != int && T != bool) {
     173           1 :       if (_toString == null) {
     174           1 :         throw ArgumentError(
     175           2 :             'Missing "toString" method for unknown type "${T.toString()}".');
     176             :       }
     177             :     }
     178             :   }
     179             : 
     180             :   // ...........................................................................
     181             :   final StreamController<T> _controller = StreamController<T>.broadcast();
     182           1 :   void _initController() {
     183           5 :     _dispose.add(() => _controller.close());
     184             :   }
     185             : 
     186             :   // ...........................................................................
     187             :   T _value;
     188             : 
     189             :   // ...........................................................................
     190             :   bool _isAlreadyTriggered = false;
     191             : 
     192             :   // ...........................................................................
     193             :   final T Function(String)? _parse;
     194             : 
     195             :   // ...........................................................................
     196             :   final String Function(T)? _toString;
     197             : 
     198             :   // ...........................................................................
     199             :   /// Set a custom comparison operator
     200             :   final bool Function(T a, T b)? compare;
     201             : }

Generated by: LCOV version 1.14