diff --git a/codegen/gql_tristate_value/lib/src/value.dart b/codegen/gql_tristate_value/lib/src/value.dart index 9029a173..00091f49 100644 --- a/codegen/gql_tristate_value/lib/src/value.dart +++ b/codegen/gql_tristate_value/lib/src/value.dart @@ -9,6 +9,10 @@ sealed class Value { /// The value is absent. It will not be serialized. const factory Value.absent() = AbsentValue; + /// If the value is non-null, it will be serialized as the value. + /// The value is absent. It will not be serialized. + const factory Value.valueOrAbsent(T? value) = ValueOrAbsent; + /// The value is present. It may still be be null. /// If the value is null, it will be serialized as null. /// If the value is non-null, it will be serialized as the value. @@ -18,6 +22,8 @@ sealed class Value { T? get requireValue => switch (this) { PresentValue(:final value) => value, AbsentValue() => throw StateError("Value is absent"), + ValueOrAbsent(:final value) => + value ?? (throw StateError("Value is absent")), }; /// return the value if present and non-null, otherwise null. @@ -25,11 +31,13 @@ sealed class Value { T? get valueOrNull => switch (this) { PresentValue(:final value) => value, AbsentValue() => null, + ValueOrAbsent(:final value) => value, }; bool get isPresent => switch (this) { PresentValue() => true, AbsentValue() => false, + ValueOrAbsent(:final value) => value == null ? false : true, }; } @@ -37,6 +45,22 @@ class AbsentValue extends Value { const AbsentValue() : super._(); } +class ValueOrAbsent extends Value { + final T? value; + + const ValueOrAbsent(this.value) : super._(); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is ValueOrAbsent && + runtimeType == other.runtimeType && + value == other.value; + + @override + int get hashCode => value.hashCode; +} + class PresentValue extends Value { final T? value; diff --git a/codegen/gql_tristate_value/test/value_test.dart b/codegen/gql_tristate_value/test/value_test.dart index 7cf49418..7165d9b0 100644 --- a/codegen/gql_tristate_value/test/value_test.dart +++ b/codegen/gql_tristate_value/test/value_test.dart @@ -2,33 +2,96 @@ import 'package:gql_tristate_value/gql_tristate_value.dart'; import 'package:test/test.dart'; void main() { - test("requireValue on absent throws", () { - final absent = const AbsentValue(); + group( + 'requireValue', + () { + test("on $AbsentValue throws", () { + final absent = const AbsentValue(); - expect(() => absent.requireValue, throwsA(isA())); - }); + expect(() => absent.requireValue, throwsA(isA())); + }); - test("requireValue on present returns value", () { - final present = Value.present(42); + test("on $PresentValue with valuereturns value", () { + final present = Value.present(42); - expect(present.requireValue, equals(42)); - }); + expect(present.requireValue, equals(42)); + }); - test("requireValue on null returns null", () { - final nullValue = Value.present(null); + test("on $PresentValue with null returns null", () { + final nullValue = Value.present(null); - expect(nullValue.requireValue, isNull); - }); + expect(nullValue.requireValue, isNull); + }); - test("valueOrNull on absent returns null", () { - final absent = const AbsentValue(); + test("on $ValueOrAbsent with value returns value", () { + final present = Value.valueOrAbsent(42); - expect(absent.valueOrNull, isNull); - }); + expect(present.requireValue, equals(42)); + }); - test("valueOrNull on present returns value", () { - final present = Value.present(42); + test("on $ValueOrAbsent with null throws", () { + final nullValue = Value.valueOrAbsent(null); - expect(present.valueOrNull, equals(42)); - }); + expect(() => nullValue.requireValue, throwsA(isA())); + }); + }, + ); + + group( + 'valueOrNull ', + () { + test("on $AbsentValue returns null", () { + final absent = const AbsentValue(); + + expect(absent.valueOrNull, isNull); + }); + + test("on $PresentValue with value returns value", () { + final present = Value.present(42); + + expect(present.valueOrNull, equals(42)); + }); + + test("on $ValueOrAbsent with value returns value", () { + final present = Value.valueOrAbsent(42); + + expect(present.valueOrNull, equals(42)); + }); + + test("on $ValueOrAbsent with null returns null", () { + final present = Value.valueOrAbsent(null); + + expect(present.valueOrNull, isNull); + }); + }, + ); + + group( + 'isPresent ', + () { + test("on $AbsentValue returns false", () { + final absent = const AbsentValue(); + + expect(absent.isPresent, false); + }); + + test("on $PresentValue with value returns true", () { + final present = Value.present(42); + + expect(present.isPresent, true); + }); + + test("on $ValueOrAbsent with value returns true", () { + final present = Value.valueOrAbsent(42); + + expect(present.isPresent, true); + }); + + test("on $ValueOrAbsent with null returns false", () { + final present = Value.valueOrAbsent(null); + + expect(present.isPresent, false); + }); + }, + ); }