Repository: passsy/kt.dart (dart)

Issue:
Add removeFirstOrNull
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/remove-first-or-null.html

Pull request description:
fixes #159 removeFirstOrNull

Patch:
diff --git a/lib/src/collection/impl/list_mutable.dart b/lib/src/collection/impl/list_mutable.dart
index e1c2eb8..5342475 100644
--- a/lib/src/collection/impl/list_mutable.dart
+++ b/lib/src/collection/impl/list_mutable.dart
@@ -112,26 +112,6 @@ class DartMutableList<T> extends Object implements KtMutableList<T> {
   @override
   void operator []=(int index, T element) => set(index, element);
 
-  @override
-  T removeFirst() {
-    if (_list.isNotEmpty) {
-      final firstElement = _list.first;
-      _list.removeAt(0);
-      return firstElement;
-    }
-    throw const NoSuchElementException('List is empty.');
-  }
-
-  @override
-  T removeLast() {
-    if (_list.isNotEmpty) {
-      final lastElement = _list.last;
-      _list.removeLast();
-      return lastElement;
-    }
-    throw const NoSuchElementException('List is empty.');
-  }
-
   @override
   bool removeAll(KtIterable<T> elements) {
     var changed = false;
diff --git a/lib/src/collection/kt_list_mutable.dart b/lib/src/collection/kt_list_mutable.dart
index ff4ee52..365c205 100644
--- a/lib/src/collection/kt_list_mutable.dart
+++ b/lib/src/collection/kt_list_mutable.dart
@@ -75,16 +75,6 @@ abstract class KtMutableList<T> implements KtList<T>, KtMutableCollection<T> {
   @override
   bool remove(T element);
 
-  /// Removes the first element from this mutable list.
-  ///
-  /// Returns that removed element, or throws [NoSuchElementException] if this list is empty.
-  T removeFirst();
-
-  /// Removes the last element from this mutable list.
-  ///
-  /// Returns that removed element, or throws [NoSuchElementException] if this list is empty.
-  T removeLast();
-
   // Bulk Modification Operations
   /// Adds all of the elements of the specified collection to the end of this list.
   ///
@@ -120,10 +110,10 @@ abstract class KtMutableList<T> implements KtList<T>, KtMutableCollection<T> {
   /// Inserts an element into the list at the specified [index].
   void addAt(int index, T element);
 
-  /// Removes an element at the specified [index] from the list.
+  /// Removes and returns the element at the specified [index] from the list.
   ///
-  /// @return the element that has been removed.
-  T? removeAt(int index);
+  /// throws [IndexOutOfBoundsException] when [index] is out of bounds
+  T removeAt(int index);
 
   @override
   KtMutableListIterator<T> listIterator([int index = 0]);
@@ -151,6 +141,42 @@ extension KtMutableListExtensions<T> on KtMutableList<T> {
     }
   }
 
+  /// Removes the first element from this mutable list.
+  ///
+  /// Returns that removed element, or throws [NoSuchElementException] if this list is empty.
+  T removeFirst() {
+    if (isEmpty()) {
+      throw const NoSuchElementException('List is empty.');
+    }
+    return removeAt(0);
+  }
+
+  /// Removes the first element from this mutable list.
+  ///
+  /// Returns that removed element, or `null` if this list is empty.
+  T? removeFirstOrNull() {
+    if (isEmpty()) return null;
+    return removeAt(0);
+  }
+
+  /// Removes the last element from this mutable list.
+  ///
+  /// Returns that removed element, or throws [NoSuchElementException] if this list is empty.
+  T removeLast() {
+    if (isEmpty()) {
+      throw const NoSuchElementException('List is empty.');
+    }
+    return removeAt(lastIndex);
+  }
+
+  /// Removes the last element from this mutable list.
+  ///
+  /// Returns that removed element, or throws [NoSuchElementException] if this list is empty.
+  T? removeLastOrNull() {
+    if (isEmpty()) return null;
+    return removeAt(lastIndex);
+  }
+
   /// Reverses elements in the list in-place.
   void reverse() {
     final mid = size >> 1;


Test patch:
diff --git a/test/collection/list_mutable_extensions_test.dart b/test/collection/list_mutable_extensions_test.dart
index fbde7f8..84a93ab 100644
--- a/test/collection/list_mutable_extensions_test.dart
+++ b/test/collection/list_mutable_extensions_test.dart
@@ -90,6 +90,108 @@ void testList(
     });
   });
 
+  group("removeFirst", () {
+    test("remove first item when found", () {
+      final list = mutableListOf("1", "2", "3");
+      final result = list.removeFirst();
+      expect(list, mutableListOf("2", "3"));
+      expect(result, "1");
+    });
+    test("remove first item when length is 1", () {
+      final list = mutableListOf("1");
+      final result = list.removeFirst();
+      expect(list, mutableListOf());
+      expect(result, "1");
+    });
+    test("remove first item when null", () {
+      final list = mutableListOf(null, null);
+      final result = list.removeFirst();
+      expect(list, mutableListOf(null));
+      expect(result, null);
+    });
+    test("throw when list is empty", () {
+      final list = mutableListOf();
+      expect(() => list.removeFirst(), throwsA(isA<NoSuchElementException>()));
+    });
+  });
+
+  group("removeFirstOrNull", () {
+    test("remove first item when found", () {
+      final list = mutableListOf("1", "2", "3");
+      final result = list.removeFirstOrNull();
+      expect(list, mutableListOf("2", "3"));
+      expect(result, "1");
+    });
+    test("remove first item when length is 1", () {
+      final list = mutableListOf("1");
+      final result = list.removeFirstOrNull();
+      expect(list, mutableListOf());
+      expect(result, "1");
+    });
+    test("null when item in list is null", () {
+      final list = mutableListOf(null, null);
+      final result = list.removeFirstOrNull();
+      expect(list, mutableListOf(null));
+      expect(result, null);
+    });
+    test("null when list is empty", () {
+      final list = mutableListOf();
+      final result = list.removeFirstOrNull();
+      expect(list, mutableListOf());
+      expect(result, null);
+    });
+  });
+
+  group("removeLast", () {
+    test("remove last item when found", () {
+      final list = mutableListOf("1", "2", "3");
+      final result = list.removeLast();
+      expect(list, mutableListOf("1", "2"));
+      expect(result, "3");
+    });
+    test("remove last item when length is 1", () {
+      final list = mutableListOf("1");
+      final result = list.removeLast();
+      expect(list, mutableListOf());
+      expect(result, "1");
+    });
+    test("remove last item when null", () {
+      final list = mutableListOf(null, "2", null);
+      final result = list.removeLast();
+      expect(list, mutableListOf(null, "2"));
+      expect(result, null);
+    });
+    test("throw when list is empty", () {
+      final list = mutableListOf();
+      expect(() => list.removeLast(), throwsA(isA<NoSuchElementException>()));
+    });
+  });
+
+  group("removeLastOrNull", () {
+    test("remove last item when found", () {
+      final list = mutableListOf("1", "2", "3");
+      final result = list.removeLastOrNull();
+      expect(list, mutableListOf("1", "2"));
+      expect(result, "3");
+    });
+    test("remove last item when length is 1", () {
+      final list = mutableListOf("1");
+      final result = list.removeLastOrNull();
+      expect(list, mutableListOf());
+      expect(result, "1");
+    });
+    test("remove last item when null", () {
+      final list = mutableListOf(null, "2", null);
+      final result = list.removeLastOrNull();
+      expect(list, mutableListOf(null, "2"));
+      expect(result, null);
+    });
+    test("null when list is empty", () {
+      final list = mutableListOf();
+      expect(list.removeLastOrNull(), null);
+    });
+  });
+
   group("sorted", () {
     String lastChar(String it) {
       final last = it.runes.last;
diff --git a/test/collection/list_mutable_test.dart b/test/collection/list_mutable_test.dart
index eed0441..d116ed0 100644
--- a/test/collection/list_mutable_test.dart
+++ b/test/collection/list_mutable_test.dart
@@ -210,54 +210,4 @@ void testList(
       expect(linkedSetOf<num>(1, 2, 3), linkedSetOf<int>(1, 2, 3));
     });
   });
-
-  group("removeFirst", () {
-    test("remove first item when found", () {
-      final list = mutableListOf("1", "2", "3");
-      final result = list.removeFirst();
-      expect(list, mutableListOf("2", "3"));
-      expect(result, "1");
-    });
-    test("remove first item when length is 1", () {
-      final list = mutableListOf("1");
-      final result = list.removeFirst();
-      expect(list, mutableListOf());
-      expect(result, "1");
-    });
-    test("remove first item when null", () {
-      final list = mutableListOf(null, null);
-      final result = list.removeFirst();
-      expect(list, mutableListOf(null));
-      expect(result, null);
-    });
-    test("throw when list is empty", () {
-      final list = mutableListOf();
-      expect(() => list.removeFirst(), throwsA(isA<NoSuchElementException>()));
-    });
-  });
-
-  group("removeLast", () {
-    test("remove last item when found", () {
-      final list = mutableListOf("1", "2", "3");
-      final result = list.removeLast();
-      expect(list, mutableListOf("1", "2"));
-      expect(result, "3");
-    });
-    test("remove last item when length is 1", () {
-      final list = mutableListOf("1");
-      final result = list.removeLast();
-      expect(list, mutableListOf());
-      expect(result, "1");
-    });
-    test("remove last item when null", () {
-      final list = mutableListOf(null, "2", null);
-      final result = list.removeLast();
-      expect(list, mutableListOf(null, "2"));
-      expect(result, null);
-    });
-    test("throw when list is empty", () {
-      final list = mutableListOf();
-      expect(() => list.removeLast(), throwsA(isA<NoSuchElementException>()));
-    });
-  });
 }