Resx ⚡️

pub package documentation license: MIT

A lightweight and complete functional error-handling toolkit for Dart, featuring Result, Option, Validation, AsyncResult, stream utilities, and a minimal Loadable state.

Tiny, complete, and functional.

English | 中文

Features ✨

🚀 High Performance - Optimized for speed and memory efficiency
🔒 Type Safe - Full null safety and strong typing
🔗 Chainable - Fluent API with method chaining
📦 Batch Operations - Process collections efficiently
🎯 Validation - Accumulate multiple errors elegantly
Async Support - First-class async/await integration
🧩 Extensions - Native Dart type integrations

Core Types 🧠

Result<T, E> - Error Handling ✅/❌

Type-safe error handling inspired by Rust's Result type.

import 'package:resx/resx.dart';

// Create results
final success = Result.ok(42);
final failure = Result.err('Something went wrong');

// Chain operations
final result = Result.ok(5)
  .map((x) => x * 2)
  .flatMap((x) => x > 5 ? Result.ok(x) : Result.err('Too small'));

// Handle both cases
final message = result.fold(
  (value) => 'Success: $value',
  (error) => 'Error: $error',
);

// Exception catching
final parsed = Result.from(() => int.parse('42')); // Ok(42)
final failed = Result.from(() => int.parse('abc')); // Err(FormatException)

// Guards and swap
final guarded = Result.ok(10).ensure((v) => v > 5, 'Too small');
final swapped = Result.ok(1).swap(); // Err(1)

Option<T> - Nullable Values ❓

Safe nullable value handling with Some/None variants.

// Create options
final some = Option.some('Hello');
final none = Option<String>.none();

// Safe operations
final result = Option.some('world')
  .map((s) => s.toUpperCase())
  .filter((s) => s.length > 3)
  .unwrapOr('DEFAULT');

// From nullable
String? nullable = getValue();
final option = Option.fromNullable(nullable);

Validation<E, T> - Error Accumulation 🧰

Collect multiple validation errors instead of failing fast.

// Built-in validators
final emailValidation = Validators.email('user@example.com', 'Invalid email');
final rangeValidation = Validators.range(25, 18, 65, 'Age out of range');

// Accumulate errors
final userValidation = Validators.notEmpty('John', 'Name required')
  .and(Validators.email('invalid-email', 'Email invalid'))
  .and(Validators.range(150, 0, 120, 'Age invalid'));

// Result: Invalid(['Email invalid', 'Age invalid'])

AsyncResult<T, E> - Async Operations ⏱️

First-class async support for Result operations.

// Create async results
final asyncResult = AsyncResult.ok(42);
final fromFuture = AsyncResult.from(fetchData());

// Chain async operations
final result = await AsyncResult.ok(5)
  .map((x) => x * 2)
  .flatMap((x) => AsyncResult.ok(x + 1));

// Handle async errors safely
final safeResult = await AsyncResult.from(riskyOperation())
  .orElse((error) => AsyncResult.ok('fallback'));

// Guard async values
final ensured = await AsyncResult.ok<int, String>(10)
  .ensure((v) => v > 0, 'non-positive');

Dart Extensions 🧩

String Extensions 🔤

// Parsing with results
final number = '42'.parseInt(); // Ok(42)
final invalid = 'abc'.parseInt(); // Err(FormatException)

// Validation
final email = 'user@example.com'.validateEmail(); // Valid(...)
final url = Validators.url('https://example.com'); // Valid(...)

List, Stream and Nullable Extensions

final numbers = [1, 2, 3, 4, 5];
final results = numbers.map((x) => x.isEven ? Result.ok(x) : Result.err('odd'));
final combined = Results.sequence(results); // Ok([...]) or first Err
final (oks, errs) = Results.partition(results);

// Stream helpers
final stream = Stream.fromIterable([1,2,3]);
final asResults = stream.toResultStream<Object>();
final collected = await stream.collectToResult<Object>();

Universal and Nullable Conversions 🔁

String? nullable = getValue();
final option = nullable.toOption(); // Option<String>

final result = nullable.toResult('Value is null'); // Result<String, String>

// Universal: wrap any value fast
final r1 = 42.ok<String>();              // Result<int, String>::Ok(42)
final r2 = 'boom'.err<int>();             // Result<int, String>::Err('boom')
final o1 = 'hello'.some();                // Option<String>::Some('hello')

Batch Operations 📦

// Combine multiple results
final results = [Result.ok(1), Result.ok(2), Result.ok(3)];
final combined = Results.combine(results); // Ok([1, 2, 3])

// Partition successes and errors
final mixed = [Result.ok(1), Result.err('error'), Result.ok(3)];
final (values, errors) = Results.partition(mixed); // ([1, 3], ['error'])

// Applicative operations
final sum = Results.lift2(
  Result.ok(2),
  Result.ok(3),
  (a, b) => a + b,
); // Ok(5)

Advanced Usage 🚀

Custom Validators

// Using built-in predicate helper
final validation = Validators.predicate<String, String>(
  'test',
  (v) => v.startsWith('prefix_'),
  'Must start with prefix_',
); // Invalid(['Must start with prefix_'])

Pattern Matching

final result = Result.ok(42);

final message = result.match(
  (value) => 'Got value: $value',
  (error) => 'Got error: $error',
);

Railway-Oriented Programming

final pipeline = (String input) => Result.ok(input)
  .flatMap(validateInput)
  .flatMap(processData)
  .flatMap(saveToDatabase)
  .map(formatResponse);

final result = pipeline('user input');

Performance

Focused API, minimal indirections, idiomatic Dart sealed classes and extension types. No magic.

Installation

Add to your pubspec.yaml:

dependencies:
  resx: any

Then run:

dart pub get

Examples

Check out the example directory for comprehensive usage examples:

API Documentation

Complete API documentation with examples is available at pub.dev.

Contributing

Contributions are welcome! Please read our contributing guidelines and feel free to submit issues and pull requests.

License

This project is licensed under the MIT License - see the LICENSE file for details.


Made with 💙 by FlutterCandies

Libraries

resx
A powerful Dart library for functional error handling and type-safe operations.