blob: 99b07dc130fb6edd4d724264a3ec9f3154492e24 [file] [log] [blame] [edit]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// A wrapper type for optional values that may also be `null`. This generic
/// class can be used in argument lists for optional arguments. When called, the
/// argument state will be one of:
///
/// * `undefined` - The caller did not provide a new value
/// * Some(value) - The caller provided a new, non-`null` value
/// * None() - The caller provided a `null` value
///
/// A common usage is:
///
/// ```dart
/// class MyClass {
/// String? maybeStr;
/// List<int>? maybeList;
/// Map<String, String>? maybeMap;
///
/// MyClass({this.maybeStr, this.maybeList, this.maybeMap});
///
/// MyClass cloneWithOverrides({
/// OptionalNullable<String> maybeStr = const OptionalNullable.undefined(),
/// OptionalNullable<List<int>> maybeList = const OptionalNullable.undefined(),
/// OptionalNullable<Map<String, String>> maybeMap = const OptionalNullable.undefined(),
/// }) {
/// return MyClass(
/// maybeStr: maybeStr.or(this.maybeStr),
/// maybeList: maybeList.or(this.maybeList),
/// maybeMap: maybeMap.or(this.maybeMap),
/// );
/// }
/// }
///
/// main() {
/// final orig = MyClass(
/// maybeStr: null,
/// maybeList: [1, 2, 3],
/// maybeMap: {'door': 'wood', 'window': 'glass'},
/// );
/// final mod = orig.cloneWithOverrides(
/// maybeStr: Some('a string'),
/// maybeMap: None(),
/// );
/// assert(mod.maybeStr == 'a string');
/// assert(mod.maybeList!.length == 3);
/// assert(mod.maybeMap == null);
/// }
/// ```
class OptionalNullable<T> {
final T? _value;
const OptionalNullable._(this._value);
/// Initialize an [OptionalNullable] to an undefined state. The value of
/// an [OptionalNullable] is considered "undefined" until overridden by
/// assigning it to [Some] or [None].
const OptionalNullable.undefined() : _value = null;
/// True if the value is [Some] or [None].
bool get isDefined => this is Some || this is None;
/// True if the value is not [Some] or [None].
bool get isUndefined => !isDefined;
/// If the value is [Some], the value is returned. If [None], `null` is
/// returned. Otherwise, the value `isUndefined`, in which case the given
/// [fallback] value is returned instead.
T? or(T? fallback) {
if (isDefined) {
return _value;
} else {
return fallback;
}
}
}
/// A value wrapped in [Some] can be assigned to an [OptionalNullable] to
/// convey the value should be used instead of a default.
class Some<T> extends OptionalNullable<T> {
const Some(T value) : super._(value);
}
/// [None] can be assigned to an [OptionalNullable] to convey that `null`
/// should be used instead of a default.
class None<T> extends OptionalNullable<T> {
const None() : super._(null);
}