letOrNone<T extends Object> function

Option<T> letOrNone<T extends Object>(
  1. dynamic input
)

Attempts to convert a dynamic input to the specified type T, returning None on failure.

This is a high-level dispatcher that uses more specific let...OrNone helpers based on the target type T.

Supported types:

Implementation

Option<T> letOrNone<T extends Object>(dynamic input) {
  assert(
    !(isSubtype<T, List<dynamic>>() && !isSubtype<List<dynamic>, T>()) &&
        !(isSubtype<T, Set<dynamic>>() && !isSubtype<Set<dynamic>, T>()) &&
        !(isSubtype<T, Iterable<dynamic>>() &&
            !isSubtype<Iterable<dynamic>, T>()) &&
        !(isSubtype<T, Map<dynamic, dynamic>>() &&
            !isSubtype<Map<dynamic, dynamic>, T>()),
    'letOrNone<$T> cannot be used with specific collection types due to type safety. '
    'Only generic collection types are supported.',
  );
  // 1. Unwrap any Outcome to get the raw value.
  if (input is Outcome) {
    return switch (input.rawSync().value) {
      Ok(value: final okValue) => letOrNone<T>(NoStackOverflowWrapper(okValue)),
      Err() => const None(),
    };
  }
  final rawInput = input is NoStackOverflowWrapper ? input.value : input;

  // 2. Handle null and direct type matches upfront for performance.
  if (rawInput is T) return Some(rawInput);
  if (rawInput == null) return const None();

  // 3. Dispatch to specific conversion logic based on the target type.
  final result = () {
    if (typeEquality<T, double>() || typeEquality<T, double?>()) {
      return letDoubleOrNone(rawInput);
    } else if (typeEquality<T, int>() || typeEquality<T, int?>()) {
      return letIntOrNone(rawInput);
    } else if (typeEquality<T, bool>() || typeEquality<T, bool?>()) {
      return letBoolOrNone(rawInput);
    } else if (typeEquality<T, DateTime>() || typeEquality<T, DateTime?>()) {
      return letDateTimeOrNone(rawInput);
    } else if (typeEquality<T, Uri>() || typeEquality<T, Uri?>()) {
      return letUriOrNone(rawInput);
    } else if (isSubtype<T, List<dynamic>>()) {
      return letListOrNone<Object>(rawInput);
    } else if (isSubtype<T, Set<dynamic>>()) {
      return letSetOrNone<Object>(rawInput);
    } else if (isSubtype<T, Iterable<dynamic>>()) {
      return letIterableOrNone<Object>(rawInput);
    } else if (isSubtype<T, Map<dynamic, dynamic>>()) {
      return letMapOrNone<Object, Object>(rawInput);
    } else if (typeEquality<T, String>() || typeEquality<T, String?>()) {
      return letAsStringOrNone(rawInput);
    }
    return rawInput;
  }();

  // 4. Perform a final safe cast on the result of the conversion.
  return letAsOrNone<T>(result);
}