From ef131879746733d349dbdd1ae05f248ca63ccb61 Mon Sep 17 00:00:00 2001 From: mcmah309 Date: Thu, 7 Mar 2024 23:10:28 +0000 Subject: [PATCH] feat: Add errorkind docs --- README.md | 5 +-- lib/src/error/README.md | 50 +++++++++++++++++++++++++++ lib/src/error/error.dart | 73 +++++++++++++++++++++++++++++----------- 3 files changed, 106 insertions(+), 22 deletions(-) create mode 100644 lib/src/error/README.md diff --git a/README.md b/README.md index edea6e4..e2c219e 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 @@ -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 \ No newline at end of file diff --git a/lib/src/error/README.md b/lib/src/error/README.md new file mode 100644 index 0000000..3142953 --- /dev/null +++ b/lib/src/error/README.md @@ -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`) 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. \ No newline at end of file diff --git a/lib/src/error/error.dart b/lib/src/error/error.dart index 0b3755e..3528300 100644 --- a/lib/src/error/error.dart +++ b/lib/src/error/error.dart @@ -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 { - static final innerNumberCapture = RegExp(r'{(\d+)}'); + static final _innerNumberCapture = RegExp(r'{(\d+)}'); final E type; final List? values; ErrorKind(this.type, [this.values]) - // : assert(() { - // final format = type.template; - // if(format != null){ - // Iterable 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 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() { @@ -43,7 +68,7 @@ class ErrorKind { 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(); @@ -58,4 +83,12 @@ class ErrorKind { return "${type.runtimeType}: $replacedTemplate"; } } + + @override + bool operator ==(Object other) { + return other is ErrorKind && other.type == type; + } + + @override + int get hashCode => type.hashCode; }