-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add nullable stream and object extensions
- Loading branch information
Showing
7 changed files
with
429 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
extension NullableExtensions on Object? { | ||
/// Checks if the object is null. | ||
/// | ||
/// Returns `true` if the object is null, `false` otherwise. | ||
bool get isNull => this == null; | ||
|
||
/// Checks if the object is not null. | ||
/// | ||
/// Returns `true` if the object is not null, `false` otherwise. | ||
bool get isNotNull => this != null; | ||
|
||
/// Allows chaining a function if the object is not null. | ||
/// | ||
/// [operation] The function to apply to the object if it is not null. | ||
/// Returns the result of the function or `null` if the object is null. | ||
T? mapIfNotNull<T>(T Function(Object it) operation) { | ||
if (this != null) { | ||
return operation(this!); | ||
} | ||
return null; | ||
} | ||
|
||
/// Allows performing an action if the object is not null. | ||
/// | ||
/// [action] The function to execute if the object is not null. | ||
void run(void Function(Object it) action) { | ||
if (this != null) { | ||
action(this!); | ||
} | ||
} | ||
|
||
/// Returns the object if it is not null, otherwise returns a default value. | ||
/// | ||
/// [defaultValue] The value to return if the object is null. | ||
/// Returns the object or the default value. | ||
T orDefault<T>(T defaultValue) { | ||
if (this != null) { | ||
return this as T; | ||
} | ||
return defaultValue; | ||
} | ||
|
||
/// Maps the object to another type if it is not null. | ||
/// | ||
/// [transform] The function to apply to the object if it is not null. | ||
/// Returns the transformed object or `null` if the object is null. | ||
T? map<T>(T Function(Object it) transform) { | ||
return this != null ? transform(this!) : null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import 'package:async/async.dart' show StreamGroup, StreamZip; | ||
|
||
extension NullableStreamExtensions<T> on Stream<T>? { | ||
/// Returns the stream if it is not null, otherwise returns a stream with the | ||
/// default value. | ||
/// | ||
/// [defaultValue] The value to emit if the stream is null. | ||
Stream<T> orDefault(T defaultValue) { | ||
return this ?? Stream.value(defaultValue); | ||
} | ||
|
||
/// Maps the stream using the provided transform function if the stream is | ||
/// not null. | ||
/// | ||
/// [transform] The function to apply to each element of the stream. | ||
/// Returns the transformed stream or null if the original stream is null. | ||
Stream<R>? mapIfNotNull<R>(R Function(T) transform) { | ||
return this?.map(transform); | ||
} | ||
|
||
/// Listens to the stream if it is not null. | ||
/// | ||
/// [onData] The function to handle data events from the stream. | ||
void listenIfNotNull(void Function(T) onData) { | ||
this?.listen(onData); | ||
} | ||
|
||
/// Combines multiple streams into a single stream of lists of values. | ||
/// | ||
/// [streams] The list of streams to combine. | ||
/// Returns a stream of lists of values from the combined streams. | ||
static Stream<List<T>> combineStreams<T>(List<Stream<T>?> streams) { | ||
final nonNullStreams = streams.whereType<Stream<T>>().toList(); | ||
if (nonNullStreams.isEmpty) return Stream.value([]); | ||
return StreamZip(nonNullStreams).asBroadcastStream(); | ||
} | ||
|
||
/// Unwraps the stream, throwing an error if the stream or any value in the | ||
/// stream is null. | ||
/// | ||
/// Returns the unwrapped stream. | ||
Stream<T> unwrap() { | ||
if (this == null) { | ||
return Stream.error('Stream is null'); | ||
} | ||
return this!.map((value) { | ||
if (value == null) { | ||
throw 'Value is null'; | ||
} | ||
return value; | ||
}); | ||
} | ||
|
||
/// Executes the provided action if the stream is empty or null. | ||
/// | ||
/// [action] The function to execute if the stream is empty or null. | ||
void onEmpty(void Function() action) { | ||
if (this == null) { | ||
action(); | ||
} else { | ||
this!.isEmpty.then((isEmpty) { | ||
if (isEmpty) action(); | ||
}); | ||
} | ||
} | ||
|
||
/// Converts the stream to a broadcast stream or returns an empty stream if | ||
/// the original stream is null. | ||
/// | ||
/// Returns the broadcast stream or an empty stream. | ||
Stream<T> toBroadcastStreamOrEmpty() { | ||
return this?.asBroadcastStream() ?? const Stream.empty(); | ||
} | ||
|
||
/// Returns the first element of the stream or the default value if the stream | ||
/// is null or empty. | ||
/// | ||
/// [defaultValue] The value to return if the stream is null or empty. | ||
/// Returns the first element of the stream or the default value. | ||
Future<T> firstOrDefault(T defaultValue) async { | ||
if (this == null) return defaultValue; | ||
try { | ||
return await this!.first; | ||
} catch (_) { | ||
return defaultValue; | ||
} | ||
} | ||
|
||
/// Merges the stream with another stream. | ||
/// | ||
/// [otherStream] The stream to merge with. | ||
/// Returns the merged stream. | ||
Stream<T> mergeWith(Stream<T> otherStream) { | ||
if (this == null) return otherStream; | ||
return StreamGroup.merge([this!, otherStream]); | ||
} | ||
|
||
/// Executes the provided action if the stream is null. | ||
/// | ||
/// [action] The function to execute if the stream is null. | ||
void handleNull(void Function() action) { | ||
if (this == null) action(); | ||
} | ||
|
||
/// Filters the stream using the provided predicate if the stream is not null. | ||
/// | ||
/// [predicate] The function to test each element of the stream. | ||
/// Returns the filtered stream or null if the original stream is null. | ||
Stream<T>? filterIfNotNull(bool Function(T) predicate) { | ||
return this?.where(predicate); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import 'package:nullx/nullx.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
void main() { | ||
group('NullableExtensions', () { | ||
test('isNull should return true if the object is null', () { | ||
const Object? value = null; | ||
expect(value.isNull, isTrue); | ||
}); | ||
|
||
test('isNull should return false if the object is not null', () { | ||
// ignore: unnecessary_nullable_for_final_variable_declarations | ||
const Object? value = 'Hello'; | ||
expect(value.isNull, isFalse); | ||
}); | ||
|
||
test('isNotNull should return true if the object is not null', () { | ||
// ignore: unnecessary_nullable_for_final_variable_declarations | ||
const Object? value = 'Hello'; | ||
expect(value.isNotNull, isTrue); | ||
}); | ||
|
||
test('isNotNull should return false if the object is null', () { | ||
const Object? value = null; | ||
expect(value.isNotNull, isFalse); | ||
}); | ||
|
||
test('let should perform operation if object is not null', () { | ||
// ignore: unnecessary_nullable_for_final_variable_declarations | ||
const Object? value = 'Hello'; | ||
final result = value.mapIfNotNull((it) => (it as String).length); | ||
expect(result, 5); | ||
}); | ||
|
||
test('let should return null if object is null', () { | ||
const Object? value = null; | ||
final result = value.mapIfNotNull((it) => (it as String).length); | ||
expect(result, isNull); | ||
}); | ||
|
||
test('run should execute action if object is not null', () { | ||
// ignore: unnecessary_nullable_for_final_variable_declarations | ||
const Object? value = 'World'; | ||
String? result; | ||
value.run((it) => result = 'Hello, ${it as String}'); | ||
expect(result, 'Hello, World'); | ||
}); | ||
|
||
test('run should not execute action if object is null', () { | ||
const Object? value = null; | ||
String? result = 'Hello'; | ||
value.run((it) => result = 'World'); | ||
expect(result, 'Hello'); // Result should be unchanged | ||
}); | ||
|
||
test('orDefault should return default if object is null', () { | ||
const Object? value = null; | ||
final result = value.orDefault('Default'); | ||
expect(result, 'Default'); | ||
}); | ||
|
||
test('orDefault should return object if it is not null', () { | ||
// ignore: unnecessary_nullable_for_final_variable_declarations | ||
const Object? value = 'Existing'; | ||
final result = value.orDefault('Default'); | ||
expect(result, 'Existing'); | ||
}); | ||
|
||
test('map should transform object if it is not null', () { | ||
// ignore: unnecessary_nullable_for_final_variable_declarations | ||
const Object? value = 'Flutter'; | ||
final result = value.map((it) => (it as String).length); | ||
expect(result, 7); | ||
}); | ||
|
||
test('map should return null if object is null', () { | ||
const Object? value = null; | ||
final result = value.map((it) => (it as String).length); | ||
expect(result, isNull); | ||
}); | ||
}); | ||
} |
Oops, something went wrong.