safe_result 2.0.0 copy "safe_result: ^2.0.0" to clipboard
safe_result: ^2.0.0 copied to clipboard

A Dart package providing a Result type for handling success and error cases in a type-safe way, with support for custom error types.

safe_result #

A Dart package providing a Result type for handling success and error cases in a type-safe way, with support for custom error types.

Features #

  • Type-safe error handling with custom error types
  • Pattern matching support
  • Functional programming utilities
  • Async operation support
  • Null safety
  • Generic error types for better error handling

Getting started #

Add the package to your pubspec.yaml:

dependencies:
  safe_result: ^2.0.0

Usage #

import 'package:safe_result/safe_result.dart';

// Define custom error types
sealed class AppError {
  final String message;
  const AppError(this.message);

  @override
  String toString() => message;
}

class ValidationError extends AppError {
  const ValidationError(super.message);
}

class NetworkError extends AppError {
  final int statusCode;
  const NetworkError(super.message, this.statusCode);

  @override
  String toString() => '$message (Status: $statusCode)';
}

// Creating Results with specific error types
final success = Result<int, ValidationError>.ok(42);
final failure = Result<int, ValidationError>.error(
    ValidationError('Value must be positive')
);

// Pattern matching
switch (success) {
  case Ok(value: final v):
    print('Value: $v');
  case Error(error: final e):
    print('Error: $e');
}

// Using map for transformation
final doubled = success.map((value) => value * 2);

// Using fold for transformation with error handling
final message = success.fold(
  onOk: (value) => 'Success: $value',
  onError: (error) => 'Error: ${error.message}',
);

// Using when for pattern matching with specific return types
final state = success.when(
  ok: (value) => UserState(
    isLoading: false,
    value: value,
    error: null,
  ),
  error: (error) => UserState(
    isLoading: false,
    value: null,
    error: error,
  ),
);

// Safe error access with type checking
try {
  final error = failure.error; // Safe: failure is Error
  print('Error occurred: $error');
} on TypeError {
  print('Cannot access error on Ok result');
}

// Handling nullable values with specific error types
final Result<String?, ValidationError> nullableResult = Result.ok(null);
final Result<String, ValidationError> nonNullResult = nullableResult
    .requireNotNull(ValidationError('Value cannot be null'));

// Async operations with NetworkError
Future<Result<User, NetworkError>> getUser() async {
  try {
    final user = await api.fetchUser();
    return Result.ok(user);
  } catch (e) {
    return Result.error(NetworkError('Failed to fetch user', 404));
  }
}

// Chaining async operations with consistent error types
final userSettings = await getUser()
    .andThen((user) => fetchUserSettings(user.id))
    .mapAsync((settings) async {
      await cache.save(settings);
      return settings;
    });

// Validation example
Result<User, ValidationError> validateUser(String name, int age, String email) {
  return switch ((name.isEmpty, age < 0, !email.contains('@'))) {
    (true, _, _) => Result.error(ValidationError('Name cannot be empty')),
    (_, true, _) => Result.error(ValidationError('Age cannot be negative')),
    (_, _, true) => Result.error(ValidationError('Invalid email format')),
    _ => Result.ok(User(name, age, email))
  };
}

Error Handling Best Practices #

  1. Define Domain-Specific Error Types

    sealed class ApiError extends AppError { ... }
    sealed class DomainError extends AppError { ... }
    sealed class ValidationError extends AppError { ... }
    
  2. Use Pattern Matching with Error Types

    switch (result) {
      case Error(error: NetworkError e) when e.statusCode == 404:
        print('Resource not found: ${e.message}');
      case Error(error: ValidationError e):
        print('Validation failed: ${e.message}');
      case Ok(value: final v):
        print('Success: $v');
    }
    
  3. Chain Operations with Consistent Error Types

    Result<T, E> validateAndTransform<T, E extends AppError>(T input) {
      return validate(input)
          .map(transform)
          .fold(
            onOk: (value) => Result.ok(value),
            onError: (error) => Result.error(error),
          );
    }
    

Additional information #

For more examples and detailed documentation, visit the GitHub repository.

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

3
likes
150
points
62
downloads

Publisher

verified publishernolann-dev.xyz

Weekly Downloads

A Dart package providing a Result type for handling success and error cases in a type-safe way, with support for custom error types.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

More

Packages that depend on safe_result