Skip to content

Commit

Permalink
feat: Add errorkind docs
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmah309 committed Mar 7, 2024
1 parent 3fd0b32 commit ef13187
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 22 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Rust's functionalities are carefully adapted to Dart's paradigms, focusing on a
## Highlights
### Libraries

| [Array] | [Cell] | [Iter] | [Option] | [Panic] | [Result] | [Slice] | [Typedefs] |
| [Array] | [Cell] | [Error] | [Iter] | [Option] | [Panic] | [Result] | [Slice] | [Typedefs] |

🔥 **Extensive Extensions:** Dozens of additional extensions with hundreds of methods tailored for Dart. These
extensions are designed for maximum composability, addressing specific scenarios.
Expand All @@ -34,6 +34,7 @@ it's also available for `T?`.


[Cell]: https://github.com/mcmah309/rust_core/tree/master/lib/src/cell
[Error]: https://github.com/mcmah309/rust_core/tree/master/lib/src/error
[Option]: https://github.com/mcmah309/rust_core/tree/master/lib/src/option
[Panic]: https://github.com/mcmah309/rust_core/tree/master/lib/src/panic
[Result]: https://github.com/mcmah309/rust_core/tree/master/lib/src/result
Expand All @@ -44,6 +45,6 @@ it's also available for `T?`.


[anyhow]: https://pub.dev/packages/anyhow
[anyhow_logging]: https://pub.dev/packages/anyhow_logging
[anyhow_logging]: https://pub.dev/packages/rewind
[rust_std]: https://pub.dev/packages/rust_std
[tapper]: https://pub.dev/packages/tapper
50 changes: 50 additions & 0 deletions lib/src/error/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Error

Error contains abstractions for working with errors (aka exceptions).

## ErrorKind

`ErrorKind` is used to represent an error. This is usually used as the Error/Failure type of a `Result`.
It similar in functionality to a Rust enum with "thiserror" crate functionality, yet unfortunately not as expressive
due to Dart language constraints.

### How To Use
To use `ErrorKind`, have an enum implement `ErrorEnum`. Each template {number} will be replaced by the corresponding
value at the index provided to `ErrorKind` if `toString()` is called. Don't worry this will never have a runtime error if you forget to provide a value.
```dart
enum IoError implements ErrorEnum {
diskRead("Could not read '{0}' from disk."),
diskWrite("Could not write '{0}' to '{1}' on disk."),
unknown;
@override
final String? template;
const IoError([this.template]);
}
void main(){
// can be any object, here a string is used.
final diskpath = "/home/user/file";
final ioError = ErrorKind(IoError.diskRead, [diskpath]);
// toString() -> "IoError: Could not read '/home/user/file' from disk.
switch(ioError.type){
case IoError.diskRead:
// code here
case IoError.diskWrite:
// code here
case IoError.unkown:
// code here
}
}
```

### Advantage vs A Rust Enum
`ErrorKind` does have some advantages over the Rust enum. It _can_ remain untyped (`ErrorKind<Enum>`) and allow you to compose errors of
different types if you so desire. Although some may warn against doing so as you lose the explicit error type until you
check.

### Alternatives
An alternative to using `ErrorKind` is hand rolling your own sealed classes. That is totally valid yet more verbose.

You can also use the [anyhow](https://pub.dev/packages/anyhow) package.
73 changes: 53 additions & 20 deletions lib/src/error/error.dart
Original file line number Diff line number Diff line change
@@ -1,34 +1,59 @@
/// An error enum with an optional template. e.g. "Could not write {0} to {1} on disk".
/// To be implemented by an [Enum] and used by [ErrorKind].
abstract class ErrorEnum implements Enum {
final String? template;

ErrorEnum([this.template]);
}

/// An abstraction to write concise errors. e.g.
/// ```dart
/// enum IoError implements ErrorEnum {
/// diskRead("Could not read '{0}' from disk."),
/// diskWrite("Could not write '{0}' to '{1}' on disk."),
/// unknown;
///
/// @override
/// final String? template;
///
/// const IoError([this.template]);
///}
///
/// void main(){
/// final diskpath = "/home/user/file";
/// final ioError = ErrorKind(IoError.diskRead, [diskpath]);
/// // toString() -> "IoError: Could not read '/home/user/file' from disk.
/// switch(ioError.type){
/// case IoError.diskRead:
/// ...
/// }
/// }
/// ```
class ErrorKind<E extends ErrorEnum> {
static final innerNumberCapture = RegExp(r'{(\d+)}');
static final _innerNumberCapture = RegExp(r'{(\d+)}');

final E type;
final List<Object>? values;

ErrorKind(this.type, [this.values])
// : assert(() {
// final format = type.template;
// if(format != null){
// Iterable<RegExpMatch> matches = innerNumberCapture.allMatches(format);

// for (var match in matches) {
// int index = int.parse(match.group(1)!);
// if(values == null){
// throw "An $ErrorKind class with type ${type.runtimeType}, expects a value at index '$index' but values was null.";
// }
// if(values.length - 1 > index){
// throw "An $ErrorKind class with type ${type.runtimeType}, expects a value at index '$index' but values only ${values.length} values were provided.";
// }
// }
// }
// return true;
// }())
;
// : assert(() {
// final format = type.template;
// if(format != null){
// Iterable<RegExpMatch> matches = innerNumberCapture.allMatches(format);

// for (var match in matches) {
// int index = int.parse(match.group(1)!);
// if(values == null){
// throw "An $ErrorKind class with type ${type.runtimeType}, expects a value at index '$index' but values was null.";
// }
// if(values.length - 1 > index){
// throw "An $ErrorKind class with type ${type.runtimeType}, expects a value at index '$index' but values only ${values.length} values were provided.";
// }
// }
// }
// return true;
// }())
;

@override
String toString() {
Expand All @@ -43,7 +68,7 @@ class ErrorKind<E extends ErrorEnum> {
if (template == null) {
return "${type.runtimeType}";
}
final replacedTemplate = template.replaceAllMapped(innerNumberCapture, (Match match) {
final replacedTemplate = template.replaceAllMapped(_innerNumberCapture, (Match match) {
int index = int.parse(match.group(1)!);
if (index < values!.length) {
return values![index].toString();
Expand All @@ -58,4 +83,12 @@ class ErrorKind<E extends ErrorEnum> {
return "${type.runtimeType}: $replacedTemplate";
}
}

@override
bool operator ==(Object other) {
return other is ErrorKind && other.type == type;
}

@override
int get hashCode => type.hashCode;
}

0 comments on commit ef13187

Please sign in to comment.