diff --git a/CHANGELOG.md b/CHANGELOG.md index b4f12a3..a1e4bca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## [2.0.5] - 11/03/21 +- `delay` await 3.seconds.delay(() { some code } +- `added getTextSize` - A good use-case will be drawing a divider with the exect width of the text above it. +- `isVideo` - Checks if string is an video file. +- `isAudio` - Checks if string is an audio file. +- `isImage` - Checks if string is an image file. +- `isNumericOnly` - Check if the string has any number in it. +- `isAlphabetOnly` - Checks if string consist only Alphabet. (No Whitespace) +- `hasCapitalletter` - Checks if string contains at least one Capital Letter. +- `isHTML` - Checks if string is an html file. +- `isEmail` - Checks if string is email.. +- `isPhoneNumber` - Checks if string is phone number, good for login checks. +- `isUsername` - Checks if string is a valid username, good for login checks. +- `isCurrency` - Checks if string is Currency. +- `isPalindrom` - Checks if string is Palindrom. (good to know for interviews as well) + + ## [2.0.0] - 11/03/21 - `Support flutter 2` - `Support null safety` diff --git a/README.md b/README.md index 7072a60..69b5606 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,23 @@ -[![](https://img.shields.io/badge/ver-2.0.1-brightgreen)](https://github.com/droididan/dart_extentions) ![](https://img.shields.io/badge/Code%20Coverage-96%25-green) ![](https://img.shields.io/badge/Bitrise-Pass-green) +[![](https://img.shields.io/badge/ver-2.0.5-green)](https://github.com/droididan/dart_extentions) ![](https://img.shields.io/badge/Code%20Coverage-96%25-green) ![](https://img.shields.io/badge/Bitrise-Pass-green) ## What New -* `SizedBox` extension - convert to percents 💪🏻 -* `Sound null-safety` added! 💪🏻 -* `BuildContext` extensions 💪🏻 -* `List` extensions 💪🏻 -* `Text` extensions 💪🏻 -* `Icon` extensions 💪🏻 -* `.sortBy` [0.3.5] Sorts elements in the array in-place according to natural sort order of the value returned by specified selector function. -* `.withTooltip` Tooltips improve the accessibility of visual widgets by proving a textual representation of the widget - +* `Iterables` -> `all` -> Returns `true` if all elements match the given +* `isVideo` - Checks if string is an video file. +* `isAudio` - Checks if string is an audio file. +* `isImage` - Checks if string is an image file. +* `isNumericOnly` - Check if the string has any number in it. +* `isAlphabetOnly` - Checks if string consist only Alphabet. (No Whitespace) +* `hasCapitalletter` - Checks if string contains at least one Capital Letter. +* `isHTML` - Checks if string is an html file. +* `isEmail` - Checks if string is email.. +* `isPhoneNumber` - Checks if string is phone number, good for login checks. +* `isUsername` - Checks if string is a valid username, good for login checks. +* `isCurrency` - Checks if string is Currency. +* `isPalindrom` - Checks if string is Palindrom. (good to know for interviews + +as well) Why Method Extensions? When you’re using someone else’s API or when you implement a library that’s widely used, it’s often impractical or impossible to change the API. But you might still want to add some functionality. *let me know if you want something specific or you found a bug at bar.idan@gmail.com* diff --git a/lib/dart_extensions.dart b/lib/dart_extensions.dart index 550c59b..ee7ebdc 100644 --- a/lib/dart_extensions.dart +++ b/lib/dart_extensions.dart @@ -30,6 +30,6 @@ export 'package:dart_extensions/src/iterable.dart'; export 'package:dart_extensions/src/ranges.dart'; export 'package:dart_extensions/src/search_algorithms.dart'; export 'package:dart_extensions/src/sort_algorithms.dart'; -export 'package:dart_extensions/src/strings.dart'; +export 'package:dart_extensions/src/string_ext.dart'; export 'package:dart_extensions/src/flutter/widgets.dart'; diff --git a/lib/emum.dart b/lib/emum.dart new file mode 100644 index 0000000..a4ba19e --- /dev/null +++ b/lib/emum.dart @@ -0,0 +1,13 @@ +extension EnumExt on dynamic { + static bool _isEnumItem(enumItem) { + final splitEnum = enumItem.toString().split('.'); + return splitEnum.length > 1 && splitEnum[0] == enumItem.runtimeType.toString(); + } + + String convertToString({bool camelCase = false}) { + assert(this != null); + assert(_isEnumItem(this), '$this of type ${this.runtimeType.toString()} is not an enum item'); + final temp = this.toString().split('.')[1]; + return !camelCase ? temp : camelCaseToWords(temp); + } +} diff --git a/lib/src/flutter/duration.dart b/lib/src/flutter/duration.dart new file mode 100644 index 0000000..4a59735 --- /dev/null +++ b/lib/src/flutter/duration.dart @@ -0,0 +1,14 @@ +import 'dart:async'; + +extension DurationExt on Duration { + /// Utility to delay some callback (or code execution). + /// + /// Sample: + /// ``` + /// await 3.seconds.delay(() { + /// .... + /// } + /// + ///``` + Future delay([FutureOr callback()?]) async => Future.delayed(this, callback); +} diff --git a/lib/src/http.dart b/lib/src/http.dart index be8101e..8095a29 100644 --- a/lib/src/http.dart +++ b/lib/src/http.dart @@ -12,8 +12,7 @@ */ import 'dart:convert' as convert; - -import 'package:dart_extensions/src/strings.dart'; +import '../src/string_ext.dart'; import 'package:http/http.dart' as http; const _defaultHeaders = {"Content-type": "application/json"}; @@ -46,7 +45,7 @@ extension HttpExtensions on String { /// current string is www.mydomain.com /// endpoint param - user /// result request -> www.mydomain.com/user - Future httpPost(String endPoint,String json, [Map headers = _defaultHeaders]) async { + Future httpPost(String endPoint, String json, [Map headers = _defaultHeaders]) async { if (this.isEmptyOrNull) return; try { @@ -66,7 +65,7 @@ extension HttpExtensions on String { /// current string is www.mydomain.com /// endpoint param - user /// result request -> www.mydomain.com/user - Future httpPut(String endPoint,String json, [Map headers = _defaultHeaders]) async { + Future httpPut(String endPoint, String json, [Map headers = _defaultHeaders]) async { if (this.isEmptyOrNull) return; try { @@ -86,7 +85,7 @@ extension HttpExtensions on String { /// current string is www.mydomain.com /// endpoint param - user /// result request -> www.mydomain.com/user - Future httpDelete(String endPoint,{Map? headers}) async { + Future httpDelete(String endPoint, {Map? headers}) async { if (this.isEmptyOrNull) return; try { diff --git a/lib/src/int.dart b/lib/src/int.dart index e6dee5a..67339ae 100644 --- a/lib/src/int.dart +++ b/lib/src/int.dart @@ -23,6 +23,7 @@ extension IntExtensions on int { return this; } + // ignore: unnecessary_null_comparison bool get isNull => (this == null); /// Returns the absolute value diff --git a/lib/src/iterable.dart b/lib/src/iterable.dart index 364bd1a..fba5aca 100644 --- a/lib/src/iterable.dart +++ b/lib/src/iterable.dart @@ -18,9 +18,53 @@ import 'package:quiver/iterables.dart'; import 'data_stractures/stack.dart'; import 'equality.dart'; -extension CollectionsExtensions on Iterable { - +extension CollectionsNullableExtension on Iterable? { + + /// Returns this Iterable if it's not `null` and the empty list otherwise. + Iterable orEmpty() => this ?? Iterable.empty(); + + ///Returns `true` if this nullable iterable is either null or empty. + bool get isEmptyOrNull => this?.isEmpty ?? true; + + /// Returns `true` if at least one element matches the given [predicate]. + bool any(bool predicate(T element)) { + if (this.isEmptyOrNull) return false; + for (final element in this.orEmpty()) if (predicate(element)) return true; + return false; + } + + /// Return a list concatenates the output of the current list and another [iterable] + List concatWithSingleList(Iterable iterable) { + if (isEmptyOrNull || iterable.isEmptyOrNull) return []; + + return [...this.orEmpty(), ...iterable]; + } + + /// Return a list concatenates the output of the current list and multiple [iterables] + List concatWithMultipleList(List> iterables) { + if (isEmptyOrNull || iterables.isEmptyOrNull) return []; + final list = iterables.toList(growable: false).expand((i) => i); + return [...this.orEmpty(), ...list]; + } + /// Zip is used to combine multiple iterables into a single list that contains + /// the combination of them two. + zip(Iterable iterable) sync* { + if (iterable.isEmptyOrNull) return; + final iterables = List.empty(growable: true) + ..add(this.orEmpty()) + ..add(iterable); + + final iterators = iterables.map((e) => e.iterator).toList(growable: false); + while (iterators.every((e) => e.moveNext())) { + yield iterators.map((e) => e.current).toList(growable: false); + } + } +} + +typedef IndexedPredicate = bool Function(int index, T); + +extension CollectionsExtensions on Iterable { ///Sorts elements in the array in-place according to natural sort order of the value returned by specified [selector] function. Iterable sortBy( TKey Function(T) keySelector, { @@ -29,13 +73,6 @@ extension CollectionsExtensions on Iterable { return InternalOrderedIterable(this, keySelector, keyComparer, false); } - /// Returns `true` if at least one element matches the given [predicate]. - bool any(bool predicate(T element)) { - if (this.isEmptyOrNull) return false; - for (final element in this) if (predicate(element)) return true; - return false; - } - /// Convert iterable to set Set toMutableSet() => Set.from(this); @@ -91,7 +128,7 @@ extension CollectionsExtensions on Iterable { List takeOnly(int n) { if (n == 0) return []; - var list = List.empty(); + var list = List.empty(growable: true); var thisList = this.toList(); if (this is Iterable) { final resultSize = this.length - n; @@ -109,7 +146,7 @@ extension CollectionsExtensions on Iterable { List drop(int n) { if (n == 0) return []; - var list = List.empty(); + var list = List.empty(growable: true); var originalList = this.toList(); if (this is Iterable) { final resultSize = this.length - n; @@ -124,7 +161,7 @@ extension CollectionsExtensions on Iterable { } // Retuns map operation as a List - List mapList(E f(T e)) => this.map(f).toList(); + List mapList(E f(T e)) => this.map(f).toList(); // Takes the first half of a list List firstHalf() => take(halfLength).toList(); @@ -170,6 +207,9 @@ extension CollectionsExtensions on Iterable { /// var name = [].firstOrDefault["jack"]; // jack T firstOrDefault(T defaultValue) => firstOrNull ?? defaultValue; + /// Will retrun new [Iterable] with all elements that satisfy the predicate [predicate], + Iterable whereIndexed(IndexedPredicate predicate) => _IndexedWhereIterable(this, predicate); + /// /// Performs the given action on each element on iterable, providing sequential index with the element. /// [item] the element on the current iteration @@ -229,6 +269,17 @@ extension CollectionsExtensions on Iterable { return count; } + /// Returns `true` if all elements match the given predicate. + /// Example: + /// [5, 19, 2].all(isEven), isFalse) + /// [6, 12, 2].all(isEven), isTrue) + bool all(bool predicate(T pred)?) { + for (var e in this) { + if (!predicate!(e)) return false; + } + return true; + } + /// Returns a list containing only the elements from given collection having distinct keys. /// /// Basically it's just like distinct function but with a predicate @@ -250,7 +301,7 @@ extension CollectionsExtensions on Iterable { /// 36 Ran List distinctBy(predicate(T selector)) { final set = HashSet(); - final list = List.empty(); + final List list = []; toList().forEach((e) { final key = predicate(e); if (set.add(key)) { @@ -308,20 +359,6 @@ extension CollectionsExtensions on Iterable { return stack; } - bool get isEmptyOrNull => this == null || isEmpty; - - /// Zip is used to combine multiple iterables into a single list that contains - /// the combination of them two. - zip(Iterable iterable) sync* { - if (iterable.isEmptyOrNull) return; - final iterables = List.empty()..add(this)..add(iterable); - - final iterators = iterables.map((e) => e.iterator).toList(growable: false); - while (iterators.every((e) => e.moveNext())) { - yield iterators.map((e) => e.current).toList(growable: false); - } - } - /// Splits the Iterable into chunks of the specified size /// /// example: @@ -330,23 +367,8 @@ extension CollectionsExtensions on Iterable { /// ([1, 2, 3], [4, 5, 6], [7, 8, 9], [10]) Iterable> chunks(int size) => partition(this, size); - /// Return a list concatenates the output of the current list and another [iterable] - List concatWithSingleList(Iterable iterable) { - if (isEmptyOrNull || iterable.isEmptyOrNull) return []; - - return [...this, ...iterable]; - } - - /// Return a list concatenates the output of the current list and multiple [iterables] - List concatWithMultipleList(List> iterables) { - if (isEmptyOrNull || iterables.isEmptyOrNull) return []; - final list = iterables.toList(growable: false).expand((i) => i); - return [...this, ...list]; - } - /// Creates a Map instance in which the keys and values are computed from the iterable. - Map associate(key(element), value(element)) => - Map.fromIterable(this, key: key, value: value); + Map associate(key(element), value(element)) => Map.fromIterable(this, key: key, value: value); /// Returns the first element matching the given [predicate], or `null` /// if element was not found. @@ -360,3 +382,36 @@ extension CollectionsExtensions on Iterable { return null; } } + +// A lazy [Iterable] skip elements do **NOT** match the predicate [_f]. +class _IndexedWhereIterable extends Iterable { + final Iterable _iterable; + final IndexedPredicate _f; + + _IndexedWhereIterable(this._iterable, this._f); + + @override + Iterator get iterator => _IndexedWhereIterator(_iterable.iterator, _f); +} + +/// [Iterator] for [_IndexedWhereIterable] +class _IndexedWhereIterator extends Iterator { + final Iterator _iterator; + final IndexedPredicate _f; + int _index = 0; + + _IndexedWhereIterator(this._iterator, this._f); + + @override + bool moveNext() { + while (_iterator.moveNext()) { + if (_f(_index++, _iterator.current)) { + return true; + } + } + return false; + } + + @override + E get current => _iterator.current; +} diff --git a/lib/src/model/user.dart b/lib/src/model/user.dart index 57be87f..5df6f80 100644 --- a/lib/src/model/user.dart +++ b/lib/src/model/user.dart @@ -17,6 +17,19 @@ class User { User(this.age,this.name); + + @override + bool operator ==(other) { + return (other is User) + && other.name == name + && other.age == age; + } + + + @override + int get hashCode => age.hashCode ^ name.hashCode; + + @override String toString() { return "$age, $name"; diff --git a/lib/src/strings.dart b/lib/src/string_ext.dart similarity index 51% rename from lib/src/strings.dart rename to lib/src/string_ext.dart index 46ef638..85f81ed 100644 --- a/lib/src/strings.dart +++ b/lib/src/string_ext.dart @@ -11,6 +11,7 @@ * limitations under the License. */ +import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; enum Gender { male, female } @@ -18,18 +19,168 @@ enum Gender { male, female } class Message { final String male, female, other; - Message({required this.male,required this.female,required this.other}) + Message({required this.male, required this.female, required this.other}) : assert(male.isEmptyOrNull), assert(female.isEmptyOrNull), assert(other.isEmptyOrNull); } extension StringExtensions on String? { + bool isNull(dynamic value) => value == null; + + /// Returns whether a dynamic value PROBABLY + /// has the isEmpty getter/method by checking + /// standard dart types that contains it. + /// + /// This is here to for the 'DRY' + bool? _isEmpty(dynamic value) { + if (value is String) { + return value.toString().trim().isEmpty; + } + if (value is Iterable || value is Map) { + return value.isEmpty as bool?; + } + return false; + } + + + + /// Checks if string is a valid username. + bool isUsername(String s) => hasMatch(s, r'^[a-zA-Z0-9][a-zA-Z0-9_.]+[a-zA-Z0-9]$'); + + /// Checks if string is Palindrom. + bool isPalindrom(String string) { + final cleanString = string.toLowerCase().replaceAll(RegExp(r"\s+"), '').replaceAll(RegExp(r"[^0-9a-zA-Z]+"), ""); + + for (var i = 0; i < cleanString.length; i++) { + if (cleanString[i] != cleanString[cleanString.length - i - 1]) { + return false; + } + } + + return true; + } + + /// Checks if string is Currency. + bool isCurrency(String s) => hasMatch(s, + r'^(S?\$|\₩|Rp|\¥|\€|\₹|\₽|fr|R\$|R)?[ ]?[-]?([0-9]{1,3}[,.]([0-9]{3}[,.])*[0-9]{3}|[0-9]+)([,.][0-9]{1,2})?( ?(USD?|AUD|NZD|CAD|CHF|GBP|CNY|EUR|JPY|IDR|MXN|NOK|KRW|TRY|INR|RUB|BRL|ZAR|SGD|MYR))?$'); + + /// Checks if string is phone number. + bool isPhoneNumber(String s) { + if (s.length > 16 || s.length < 9) return false; + return hasMatch(s, r'^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$'); + } + + /// Checks if string is email. + bool isEmail(String s) => hasMatch(s, + r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'); + + /// Checks if string is an html file. + bool isHTML(String filePath) { + return filePath.toLowerCase().endsWith(".html"); + } + + /// Checks if string is an video file. + bool isVideo(String filePath) { + var ext = filePath.toLowerCase(); + + return ext.endsWith(".mp4") || + ext.endsWith(".avi") || + ext.endsWith(".wmv") || + ext.endsWith(".rmvb") || + ext.endsWith(".mpg") || + ext.endsWith(".mpeg") || + ext.endsWith(".3gp"); + } + + /// Checks if string is an audio file. + bool isAudio(String filePath) { + final ext = filePath.toLowerCase(); + + return ext.endsWith(".mp3") || + ext.endsWith(".wav") || + ext.endsWith(".wma") || + ext.endsWith(".amr") || + ext.endsWith(".ogg"); + } + + /// Checks if string is an image file. + bool isImage(String filePath) { + final ext = filePath.toLowerCase(); + + return ext.endsWith(".jpg") || + ext.endsWith(".jpeg") || + ext.endsWith(".png") || + ext.endsWith(".gif") || + ext.endsWith(".bmp"); + } + + bool hasMatch(String? value, String pattern) { + return (value == null) ? false : RegExp(pattern).hasMatch(value); + } + + // Check if the string has any number in it, not accepting double, so don't + // use "." + isNumericOnly(String s) => hasMatch(s, r'^\d+$'); + + /// Checks if string consist only Alphabet. (No Whitespace) + bool isAlphabetOnly(String s) => hasMatch(s, r'^[a-zA-Z]+$'); + + /// Checks if string contains at least one Capital Letter + bool hasCapitalletter(String s) => hasMatch(s, r'[A-Z]'); + + /// Checks if data is null or blank (empty or only contains whitespace). + bool? isBlank(dynamic value) { + return _isEmpty(value); + } + + /// Checks if string is boolean. + bool isBool(String value) { + if (isNull(value)) { + return false; + } + + return (value == 'true' || value == 'false'); + } + + Size get getTextSize { + final TextPainter textPainter = TextPainter( + text: TextSpan(text: this), + maxLines: 1, + )..layout( + minWidth: 0, + maxWidth: double.infinity, + ); + return textPainter.size; + } + + // Will add new line if the sentence is bigger the 2 words. + /// [afterWords] will add new line after the selected word + /// Example + /// 'Hi, my name is'.wrapString(2) + /// + /// will print: + /// Hi, my + /// name is + + String wrapString(int afterWords) { + final wordsArr = this?.split(' ') ?? []; + + if (wordsArr.length > 2) { + final int middle = (this?.indexOf(wordsArr[afterWords]) ?? 0) - 1; + final prefix = this?.substring(0, middle); + final postfix = this?.substring(middle + 1); + return '$prefix\n$postfix'; + } + + return this ?? ''; + } + String generateMessageByGender({Gender? gender, Message? message}) => Intl.gender(gender.toString(), male: '$this ${message?.male}', female: '$this ${message?.female}', other: '$this ${message?.other}'); bool validateEmail() { - if(this == null) return false; + if (this == null) return false; return RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(this!); } @@ -38,7 +189,7 @@ extension StringExtensions on String? { /// Return the string only if the delimiter exists in both ends, otherwise it will return the current string String? removeSurrounding(String delimiter) { - if(this == null) return null; + if (this == null) return null; final prefix = delimiter; final suffix = delimiter; @@ -54,10 +205,12 @@ extension StringExtensions on String? { /// Replace part of string after the first occurrence of given delimiter with the [replacement] string. /// If the string does not contain the delimiter, returns [defaultValue] which defaults to the original string. String? replaceAfter(String delimiter, String replacement, [String? defaultValue]) { - if(this == null) return null; + if (this == null) return null; final index = this!.indexOf(delimiter); return (index == -1) - ? defaultValue.isEmptyOrNull ? this : defaultValue + ? defaultValue.isEmptyOrNull + ? this + : defaultValue : this!.replaceRange(index + 1, this!.length, replacement); } @@ -65,14 +218,18 @@ extension StringExtensions on String? { /// Replace part of string before the first occurrence of given delimiter with the [replacement] string. /// If the string does not contain the delimiter, returns [missingDelimiterValue?] which defaults to the original string. String? replaceBefore(String delimiter, String replacement, [String? defaultValue]) { - if(this == null) return null; + if (this == null) return null; final index = this!.indexOf(delimiter); - return (index == -1) ? defaultValue.isEmptyOrNull ? this : defaultValue : this!.replaceRange(0, index, replacement); + return (index == -1) + ? defaultValue.isEmptyOrNull + ? this + : defaultValue + : this!.replaceRange(0, index, replacement); } ///Returns `true` if at least one element matches the given [predicate]. /// the [predicate] should have only one character - bool anyChar(bool predicate(String element)) => this.isEmptyOrNull ? false: this!.split('').any((s) => predicate(s)); + bool anyChar(bool predicate(String element)) => this.isEmptyOrNull ? false : this!.split('').any((s) => predicate(s)); /// Returns the string if it is not `null`, or the empty string otherwise String get orEmpty => this ?? ""; @@ -92,7 +249,7 @@ extension StringExtensions on String? { } /// Parses the string as an double or returns `null` if it is not a number. - double? toDoubleOrNull() => this == null ? null :double.tryParse(this!); + double? toDoubleOrNull() => this == null ? null : double.tryParse(this!); /// Parses the string as an int or returns `null` if it is not a number. int? toIntOrNull() => this == null ? null : int.tryParse(this!); @@ -118,11 +275,9 @@ extension StringExtensions on String? { /// Shrink a string to be no more than [maxSize] in length, extending from the end. /// For example, in a string with 10 charachters, a [maxSize] of 3 would return the last 3 charachters. - String? limitFromEnd(int maxSize) => (this?.length ?? 0) < maxSize - ? this - : this!.substring(this!.length - maxSize); + String? limitFromEnd(int maxSize) => (this?.length ?? 0) < maxSize ? this : this!.substring(this!.length - maxSize); - /// Shrink a string to be no more than [maxSize] in length, extending from the start. + /// Shrink a string to be no more than [maxSize] in length, extending from the start. /// For example, in a string with 10 charachters, a [maxSize] of 3 would return the first 3 charachters. String? limitFromStart(int maxSize) => (this?.length ?? 0) < maxSize ? this : this!.substring(0, maxSize); @@ -130,7 +285,7 @@ extension StringExtensions on String? { /// /// Returns `true` if this string is any of these values: `"true"`, `"yes"`, `"1"`, or if the string is a number and greater than 0, `false` if less than 1. This is also case insensitive. bool get asBool { - var s = this?.trim().toLowerCase()??"false"; + var s = this?.trim().toLowerCase() ?? "false"; num n; try { n = num.parse(s); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index fad3b9e..f66707b 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -12,6 +12,7 @@ */ void checkNullError(Object o) { + // ignore: unnecessary_null_comparison if (o == null) { throw NullIteratorError(); } diff --git a/pubspec.yaml b/pubspec.yaml index 44f084e..12f7ae7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_extensions description: Set of method-extensions for dart that makes using framework in a much easier and clean way also adds additional functionality. -version: 2.0.1 +version: 2.0.5 documentation: https://github.com/droididan/dart_extentions homepage: https://github.com/droididan/dart_extentions @@ -16,5 +16,5 @@ dependencies: sdk: flutter dev_dependencies: - test: ^1.16.7 + test: 1.17.1 \ No newline at end of file diff --git a/test/enum_test.dart b/test/enum_test.dart new file mode 100644 index 0000000..c72c118 --- /dev/null +++ b/test/enum_test.dart @@ -0,0 +1,14 @@ +import 'package:test/expect.dart'; +import 'package:test/test.dart'; +import '../lib/emum.dart'; + +enum TestEnum { testValue1, testValue2, testValue3 } +enum OtherEnumForTesting { helloImAnEnumValue } + +void main() { + test('it should convert enums to string', () { + expect(TestEnum.testValue1.convertToString(), 'testValue1'); + expect(TestEnum.testValue2.convertToString(), 'testValue2'); + expect(TestEnum.testValue3.convertToString(), 'testValue3'); + }); +} diff --git a/test/iterable_tests.dart b/test/iterable_tests.dart index 54e4eda..502a7c8 100644 --- a/test/iterable_tests.dart +++ b/test/iterable_tests.dart @@ -17,182 +17,231 @@ import 'package:test/test.dart'; main() { final users = [User(22, "Ronit"), User(23, "Ronit"), User(22, "Oded"), User(32, "Shimi")]; - group('iterables', () { - test('mapList', () { - expect([3,4,5,6,7].mapList((item) => item.toDouble()), [3.0, 4.0, 5.0, 6.0, 7.0]); - }); + test('result of whereIndex', () { + final result = users.whereIndexed((index, _) => index == 2); + expect(result.toList(), [User(22, "Oded")]); + }); - test('any', () { - expect(users.any((u) => u.name == 'Oded'), true); - expect(users.any((u) => u.name == 'Not Exists'), false); - expect([].any((u) => u.name == 'Oded'), false); - }); + test('if all elements match the predicate', () { + final isEven = (x) => x % 2 == 0; + expect([5, 19, 2].all(isEven), isFalse); + expect([6, 12, 2].all(isEven), isTrue); + expect([].all(isEven), isTrue); + }); - test("first or null", (){ - final list= [1,45,6,9]; - expect(null, list.firstWhere((element) => element == 7)); - }); + test('mapList', () { + expect([3, 4, 5, 6, 7].mapList((item) => item.toDouble()), [3.0, 4.0, 5.0, 6.0, 7.0]); + }); - test('filter', () { - [null, 1, 2].where((n) => n == 2); - final listWithNull = [null, User(1, "r"), User(2, "t")]; - final numberList = [1, 2, 3, 4, 5, 6]; + test('any', () { + expect(users.any((u) => u.name == 'Oded'), true); + expect(users.any((u) => u.name == 'Not Exists'), false); + expect([].any((u) => u.name == 'Oded'), false); + }); - expect(users.filter((u) => u.name == "Ronit"), [users[0], users[1]]); - expect(numberList.filter((n) => n > 4), [5, 6]); - expect(listWithNull.filter((u) => u?.name == "r"), [listWithNull[1]]); - }); + test("first or null", () { + final list = [1, 45, 6, 9]; + expect(1, list.firstOrNull); + final list2 =List.empty(); + expect(null, list2.firstOrNull); - test('filterNot', () { - final listWithNull = [null, User(1, "r"), User(2, "t")]; - final numberList = [1, 2, 3, 4, 5, 6]; + }); - expect(listWithNull.filterNot((u) => u?.name == "t"), [listWithNull[1]]); - expect(numberList.filterNot((n) => n > 4), [1, 2, 3, 4]); - }); + test('filter', () { + [null, 1, 2].where((n) => n == 2); + final listWithNull = [null, User(1, "r"), User(2, "t")]; + final numberList = [1, 2, 3, 4, 5, 6]; - test('takeOnly', () { - expect([1, 2, 3, 4].takeOnly(1), [1]); - expect([1, 2, 3, 4].takeOnly(2), [1, 2]); - expect([1, 2, 3, 4].takeOnly(10), []); - }); + expect(users.filter((u) => u.name == "Ronit"), [users[0], users[1]]); + expect(numberList.filter((n) => n > 4), [5, 6]); + expect(listWithNull.filter((u) => u?.name == "r"), [listWithNull[1]]); + }); - test('drop', () { - expect([1, 2, 3, 4].drop(1), [2, 3, 4]); - expect([1, 2, 3, 4].drop(2), [3, 4]); - expect([1, 2, 3, 4].drop(5), []); - }); + test('filterNot', () { + final listWithNull = [null, User(1, "r"), User(2, "t")]; + final numberList = [1, 2, 3, 4, 5, 6]; - test('firstHalf', () { - expect([1, 2, 3, 4].firstHalf(), [1, 2]); - }); + expect(listWithNull.filterNot((u) => u?.name == "t"), [listWithNull[1]]); + expect(numberList.filterNot((n) => n > 4), [1, 2, 3, 4]); + }); - test('secondHalf', () { - expect([1, 2, 3, 4].secondHalf(), [3, 4]); - }); + test('takeOnly', () { + expect([1, 2, 3, 4].takeOnly(1), [1]); + expect([1, 2, 3, 4].takeOnly(2), [1, 2]); + expect([1, 2, 3, 4].takeOnly(10), []); + }); - test('swap', () { - expect([1, 2, 3, 4].swap(0, 1), [2, 1, 3, 4]); - }); + test('drop', () { + expect([1, 2, 3, 4].drop(1), [2, 3, 4]); + expect([1, 2, 3, 4].drop(2), [3, 4]); + expect([1, 2, 3, 4].drop(5), []); + }); - test('lastOrDefault', () { - expect([1, 2, 3, 4].lastOrDefault(999), 4); - expect([].lastOrDefault(999), 999); - }); + test('firstHalf', () { + expect([1, 2, 3, 4].firstHalf(), [1, 2]); + }); - test('firstOrDefault', () { - expect([1, 2, 3, 4].firstOrDefault(999), 1); - expect([].firstOrDefault(999), 999); - }); + test('secondHalf', () { + expect([1, 2, 3, 4].secondHalf(), [3, 4]); + }); - test('sortedDescending', () { - expect([1, 2, 3, 4].sortedDescending(), [4, 3, 2, 1]); - }); + test('swap', () { + expect([1, 2, 3, 4].swap(0, 1), [2, 1, 3, 4]); + }); - test('containsAll', () { - expect([1, 2, 3, 4].containsAll([1, 2, 5]), false); - expect([1, 2, 3, 4].containsAll([1, 2, 4]), true); - expect([].containsAll([1, 2, 4]), false); - }); + test('lastOrDefault', () { + expect([1, 2, 3, 4].lastOrDefault(999), 4); + expect([].lastOrDefault(999), 999); + }); - test('containsAll', () { - expect([1, 2, 3, 4].containsAll([1, 2, 5]), false); - expect([1, 2, 3, 4].containsAll([1, 2, 4]), true); - expect([].containsAll([1, 2, 4]), false); - }); + test('firstOrDefault', () { + expect([1, 2, 3, 4].firstOrDefault(999), 1); + expect([].firstOrDefault(999), 999); + }); - test('containsAll', () { - expect(users.count((u) => u.age == 22), 2); - expect(users.count((u) => u.name == "Ronit"), 2); - expect(users.count((u) => u.name == "Bin"), 0); - }); + test('sortedDescending', () { + expect([1, 2, 3, 4].sortedDescending(), [4, 3, 2, 1]); + }); - test('distinctBy', () { - final expected = users.distinctBy((u) => u.age); - expect(users.distinctBy((u) => u.age), expected); - }); + test('containsAll', () { + expect([1, 2, 3, 4].containsAll([1, 2, 5]), false); + expect([1, 2, 3, 4].containsAll([1, 2, 4]), true); + expect([].containsAll([1, 2, 4]), false); + }); - test('subtract', () { - expect([1, 2, 3, 4].subtract([3, 4]), [1, 2]); - expect([1, 2, 3, 4].subtract([1, 2, 3, 4]), []); - expect([1, 2, 3, 4].subtract([1, 2]), [3, 4]); - }); + test('containsAll', () { + expect([1, 2, 3, 4].containsAll([1, 2, 5]), false); + expect([1, 2, 3, 4].containsAll([1, 2, 4]), true); + expect([].containsAll([1, 2, 4]), false); + }); - test('toStack', () { - var stack = [1, 2, 3, 4].toStack(); - expect(stack.pop(), 4); - expect(stack.top(), 3); - expect(stack.pop(), 3); - expect(stack.top(), 2); - }); + test('containsAll', () { + expect(users.count((u) => u.age == 22), 2); + expect(users.count((u) => u.name == "Ronit"), 2); + expect(users.count((u) => u.name == "Bin"), 0); + }); - test('concatWithSingleList', () { - expect([1, 2, 3, 4].concatWithSingleList([5, 6, 7, 8, 9, 10, 11]), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - expect([1, 2, 3, 4].concatWithSingleList([]), []); - expect([].concatWithSingleList([5, 6, 7, 8, 9, 10, 11]), []); - }); + test('distinctBy', () { + final expected = users.distinctBy((u) => u.age); + expect(users.distinctBy((u) => u.age), expected); + }); - test('concatWithMultipleList', () { - final listOfLists = [ - [5, 6, 7], - [8, 9, 10] - ]; - expect([1, 2, 3, 4].concatWithMultipleList(listOfLists), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - expect([1, 2, 3, 4].concatWithMultipleList([[], []]), [1, 2, 3, 4]); - expect([].concatWithMultipleList(listOfLists), []); - }); + test('subtract', () { + expect([1, 2, 3, 4].subtract([3, 4]), [1, 2]); + expect([1, 2, 3, 4].subtract([1, 2, 3, 4]), []); + expect([1, 2, 3, 4].subtract([1, 2]), [3, 4]); + }); - test('zip', () { - final expectedResult = [ - [1, 2], - [3, 4], - [5, 6] - ]; - expect([1, 3, 5].zip([2, 4, 6]), expectedResult); - expect([1, 3, 5].zip([2, 4, 6, 7, 8]), expectedResult); - }); + test('toStack', () { + var stack = [1, 2, 3, 4].toStack(); + expect(stack.pop(), 4); + expect(stack.top(), 3); + expect(stack.pop(), 3); + expect(stack.top(), 2); + }); - test('associate', () { - final users = [User(33, "Miki"), User(45, "Anna"), User(19, "Amit")]; - expect(users.associate((k) => k.name, (e) => e.age), {'Miki': 33, 'Anna': 45, 'Amit': 19}); - }); + test('concatWithSingleList', () { + expect([1, 2, 3, 4].concatWithSingleList([5, 6, 7, 8, 9, 10, 11]), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + expect([1, 2, 3, 4].concatWithSingleList([]), []); + expect([].concatWithSingleList([5, 6, 7, 8, 9, 10, 11]), []); + }); - test('find', () { - final users = [User(22, "Ronit"), User(23, "Ronit"), User(22, "Oded"), User(32, "Shimi")]; - expect(users.find((u) => u.name == "Ronit"), users.first); - expect(users.find((u) => u.name == "Oded"), users[2]); - expect(users.find((u) => u.name == "Not Exists Name"), null); - expect(users.find((u) => u.age == 32), users.last); - expect(users.find((u) => u.age == 31), null); - }); + test('concatWithMultipleList', () { + final listOfLists = [ + [5, 6, 7], + [8, 9, 10] + ]; + expect([1, 2, 3, 4].concatWithMultipleList(listOfLists), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + expect([1, 2, 3, 4].concatWithMultipleList([[], []]), [1, 2, 3, 4]); + expect([].concatWithMultipleList(listOfLists), []); + }); - test('gruopBy by age', () { - final expected = { - 22: [users[0], users[2]], - 23: [users[1]], - 32: [users[3]] - }; + test('zip', () { + final expectedResult = [ + [1, 2], + [3, 4], + [5, 6] + ]; + expect([1, 3, 5].zip([2, 4, 6]), expectedResult); + expect([1, 3, 5].zip([2, 4, 6, 7, 8]), expectedResult); + }); - expect(users.groupBy((User u) => u.age), expected); - }); + test('associate', () { + final users = [User(33, "Miki"), User(45, "Anna"), User(19, "Amit")]; + expect(users.associate((k) => k.name, (e) => e.age), {'Miki': 33, 'Anna': 45, 'Amit': 19}); + }); - test('gourpBy by name', () { - final expected = { - 'Ronit': [users[0], users[1]], - 'Oded': [users[2]], - 'Shimi': [users[3]] - }; - expect(users.groupBy((User u) => u.name), expected); - }); + test('find', () { + final users = [User(22, "Ronit"), User(23, "Ronit"), User(22, "Oded"), User(32, "Shimi")]; + expect(users.find((u) => u.name == "Ronit"), users.first); + expect(users.find((u) => u.name == "Oded"), users[2]); + expect(users.find((u) => u.name == "Not Exists Name"), null); + expect(users.find((u) => u.age == 32), users.last); + expect(users.find((u) => u.age == 31), null); + }); - test('toMutableSet', () { - expect([1, 1, 1, 1, 2, 3, 4].toMutableSet(), [1, 2, 3, 4]); - expect(["a", "b", "a"].toMutableSet(), ["a", "b"]); - }); + test('gruopBy by age', () { + final expected = { + 22: [users[0], users[2]], + 23: [users[1]], + 32: [users[3]] + }; + + expect(users.groupBy((User u) => u.age), expected); + }); + + test('gourpBy by name', () { + final expected = { + 'Ronit': [users[0], users[1]], + 'Oded': [users[2]], + 'Shimi': [users[3]] + }; + expect(users.groupBy((User u) => u.name), expected); + }); + + test('toMutableSet', () { + expect([1, 1, 1, 1, 2, 3, 4].toMutableSet(), [1, 2, 3, 4]); + expect(["a", "b", "a"].toMutableSet(), ["a", "b"]); + }); test('intersect', () { expect(Set.from([1, 2, 3, 4]).intersect(Set.from([3, 4, 5, 6])), [1, 2, 3, 4, 5, 6]); expect(Set.from([-1, -2, -3, 4]).intersect(Set.from([3, 4, 5, 6])), [-1, -2, -3, 4, 3, 5, 6]); }); + + test('orEmpty',(){ + List? list; + expect(list.orEmpty(), List.empty()); + list= [1,2,3]; + expect(list.orEmpty(), [1,2,3]); + }); + + test('isEmptyOrNull',(){ + List? list; + expect(list.isEmptyOrNull, true); + list= []; + expect(list.isEmptyOrNull, true); + list= [1,2,3]; + expect(list.isEmptyOrNull, false); + }); + test('intersect', () { + expect(Set.from([1, 2, 3, 4]).intersect(Set.from([3, 4, 5, 6])), [1, 2, 3, 4, 5, 6]); + expect(Set.from([-1, -2, -3, 4]).intersect(Set.from([3, 4, 5, 6])), [-1, -2, -3, 4, 3, 5, 6]); + + test('orEmpty',(){ + List? list; + expect(list.orEmpty(), List.empty()); + list= [1,2,3]; + expect(list.orEmpty(), [1,2,3]); + }); + + test('isEmptyOrNull',(){ + List? list; + expect(list.isEmptyOrNull, true); + list= []; + expect(list.isEmptyOrNull, true); + list= [1,2,3]; + expect(list.isEmptyOrNull, false); + }); }); } diff --git a/test/strings_test.dart b/test/strings_test.dart index 7386c58..06fef30 100644 --- a/test/strings_test.dart +++ b/test/strings_test.dart @@ -12,10 +12,15 @@ */ import 'package:test/test.dart'; -import 'package:dart_extensions/src/strings.dart'; +import 'package:dart_extensions/src/string_ext.dart'; main() { group('strings', () { + test('it should wrap a string', () { + expect('one two three four'.wrapString(2), 'one two\nthree four'); + expect('one two three four'.wrapString(3), 'one two three\nfour'); + }); + test('validateEmail', () { expect('name@domain.com'.validateEmail(), true); expect('name@domain'.validateEmail(), false);