You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

52 lines
1.9 KiB
Dart

/// Sealed response type returned by every [ApiClient] call.
///
/// Use Dart 3 exhaustive pattern-matching so both outcomes are always handled:
/// ```dart
/// final result = await ApiClient.get<User>(
/// url: '/users/me',
/// fromJson: (j) => User.fromJson(j as Map<String, dynamic>),
/// );
///
/// switch (result) {
/// case ApiSuccess(:final data):
/// print('Got user: ${data.name}');
/// case ApiFailure(:final code, :final message):
/// print('Error $code: $message');
/// }
/// ```
sealed class ApiResponse<T> {}
/// Represents a successful API call whose response body was parsed into [data]
/// via the caller-supplied `fromJson` function.
final class ApiSuccess<T> extends ApiResponse<T> {
/// The parsed response payload.
final T data;
ApiSuccess(this.data);
}
/// Represents a failed API call, carrying an optional HTTP status [code] and a
/// human-readable [message].
///
/// Failure sources include:
/// * No internet connectivity → `code: null`
/// * Connection / receive timeout → `code: null` or server code
/// * HTTP error (non-2xx or `isSuccess` predicate returned `false`) → HTTP code
/// * Response JSON parsing error → HTTP code
/// * Request cancellation via `ApiClient.cancel()` → `code: -1`
/// * Client not initialized (`ApiClientNotInitializedException`) → `code: null`
/// * Unknown / unexpected error → `code: null`
final class ApiFailure<T> extends ApiResponse<T> {
/// HTTP status code when available, or `null` when the failure happened
/// before / after the network layer (e.g. no connectivity, parsing error).
///
/// A synthetic value of `-1` is used for cancellations so callers can
/// distinguish them from real HTTP errors without string-matching.
final int? code;
/// Human-readable description of the failure suitable for display.
final String message;
ApiFailure({this.code, required this.message});
}