diff --git a/packages/sane/lib/src/exceptions.dart b/packages/sane/lib/src/exceptions.dart index 7f0f6c6..d777e64 100644 --- a/packages/sane/lib/src/exceptions.dart +++ b/packages/sane/lib/src/exceptions.dart @@ -1,23 +1,202 @@ -abstract class SaneException implements Exception {} +import 'package:ffi/ffi.dart'; +import 'package:meta/meta.dart'; +import 'package:sane/src/bindings.g.dart'; +import 'package:sane/src/dylib.dart'; -class SaneUnsupportedException extends SaneException {} +/// Base class for all possible errors that can occur in the SANE library. +/// +/// See also: +/// +/// - [SaneEofException] +/// - [SaneJammedException] +/// - [SaneDeviceBusyException] +/// - [SaneInvalidDataException] +/// - [SaneIoException] +/// - [SaneNoDocumentsException] +/// - [SaneCoverOpenException] +/// - [SaneUnsupportedException] +/// - [SaneCancelledException] +/// - [SaneNoMemoryException] +/// - +sealed class SaneException implements Exception { + SANE_Status get _status; -class SaneCancelledException extends SaneException {} + const SaneException._(); -class SaneDeviceBusyException extends SaneException {} + factory SaneException(SANE_Status status) { + final exception = switch (status) { + SANE_Status.STATUS_GOOD => + throw ArgumentError( + 'Cannot create SaneException with status STATUS_GOOD', + 'status', + ), + SANE_Status.STATUS_UNSUPPORTED => SaneUnsupportedException(), + SANE_Status.STATUS_CANCELLED => SaneCancelledException(), + SANE_Status.STATUS_DEVICE_BUSY => SaneDeviceBusyException(), + SANE_Status.STATUS_INVAL => SaneInvalidDataException(), + SANE_Status.STATUS_EOF => SaneEofException(), + SANE_Status.STATUS_JAMMED => SaneJammedException(), + SANE_Status.STATUS_NO_DOCS => SaneNoDocumentsException(), + SANE_Status.STATUS_COVER_OPEN => SaneCoverOpenException(), + SANE_Status.STATUS_IO_ERROR => SaneIoException(), + SANE_Status.STATUS_NO_MEM => SaneNoMemoryException(), + SANE_Status.STATUS_ACCESS_DENIED => SaneAccessDeniedException(), + }; -class SaneInvalidException extends SaneException {} + assert(exception._status == status); -class SaneJammedException extends SaneException {} + return exception; + } -class SaneNoDocumentsException extends SaneException {} + String get message { + return dylib.sane_strstatus(_status).cast().toDartString(); + } -class SaneCoverOpenException extends SaneException {} + @override + String toString() { + return '$runtimeType: $message'; + } +} -class SaneIOErrorException extends SaneException {} +/// No more data available. +/// +/// See also: +/// +/// - +final class SaneEofException extends SaneException { + const SaneEofException() : super._(); -class SaneNoMemoryException extends SaneException {} + @override + SANE_Status get _status => SANE_Status.STATUS_EOF; +} -class SaneAccessDeniedException extends SaneException {} +/// The document feeder is jammed. +/// +/// See also: +/// +/// - +final class SaneJammedException extends SaneException { + const SaneJammedException() : super._(); -class SaneNotFoundOption extends SaneException {} + @override + SANE_Status get _status => SANE_Status.STATUS_JAMMED; +} + +/// The document feeder is out of documents. +/// +/// See also: +/// +/// - +final class SaneNoDocumentsException extends SaneException { + const SaneNoDocumentsException() : super._(); + + @override + SANE_Status get _status => SANE_Status.STATUS_NO_DOCS; +} + +/// The scanner cover is open. +/// +/// See also: +/// +/// - +final class SaneCoverOpenException extends SaneException { + const SaneCoverOpenException() : super._(); + + @override + SANE_Status get _status => SANE_Status.STATUS_COVER_OPEN; +} + +/// The device is busy. +/// +/// See also: +/// +/// - +final class SaneDeviceBusyException extends SaneException { + const SaneDeviceBusyException() : super._(); + + @override + SANE_Status get _status => SANE_Status.STATUS_DEVICE_BUSY; +} + +/// Data is invalid. +/// +/// See also: +/// +/// - +final class SaneInvalidDataException extends SaneException { + const SaneInvalidDataException() : super._(); + + @override + SANE_Status get _status => SANE_Status.STATUS_INVAL; +} + +/// Error during device I/O. +/// +/// See also: +/// +/// - +final class SaneIoException extends SaneException { + const SaneIoException() : super._(); + + @override + SANE_Status get _status => SANE_Status.STATUS_IO_ERROR; +} + +/// Out of memory. +/// +/// See also: +/// +/// - +final class SaneNoMemoryException extends SaneException { + const SaneNoMemoryException() : super._(); + + @override + SANE_Status get _status => SANE_Status.STATUS_NO_MEM; +} + +/// Access to resource has been denied. +/// +/// See also: +/// +/// - +final class SaneAccessDeniedException extends SaneException { + const SaneAccessDeniedException() : super._(); + + @override + SANE_Status get _status => SANE_Status.STATUS_ACCESS_DENIED; +} + +/// Operation was cancelled. +/// +/// See also: +/// +/// - +final class SaneCancelledException extends SaneException { + const SaneCancelledException() : super._(); + + @override + SANE_Status get _status => SANE_Status.STATUS_CANCELLED; +} + +/// Operation is not supported. +/// +/// See also: +/// +/// - +final class SaneUnsupportedException extends SaneException { + const SaneUnsupportedException() : super._(); + + @override + SANE_Status get _status => SANE_Status.STATUS_UNSUPPORTED; +} + +@internal +extension SaneStatusExtension on SANE_Status { + /// Throws [SaneException] if the status is not [SANE_Status.STATUS_GOOD]. + @pragma('vm:prefer-inline') + void check() { + if (this != SANE_Status.STATUS_GOOD) { + throw SaneException(this); + } + } +} \ No newline at end of file diff --git a/packages/sane/lib/src/sane.dart b/packages/sane/lib/src/sane.dart index d9bb9d5..7fa756a 100644 --- a/packages/sane/lib/src/sane.dart +++ b/packages/sane/lib/src/sane.dart @@ -49,7 +49,7 @@ class Sane { final status = dylib.sane_init(versionCodePointer, nativeAuthCallback); print('sane_init() -> ${status.name}'); - _handleSaneStatus(status); + status.check(); final versionCode = versionCodePointer.value; print('SANE version: ${SaneUtils.version(versionCodePointer.value)}'); @@ -90,7 +90,7 @@ class Sane { ); print('sane_get_devices() -> ${status.name}'); - _handleSaneStatus(status); + status.check(); final devices = []; for (var i = 0; deviceListPointer.value[i] != ffi.nullptr; i++) { @@ -115,7 +115,7 @@ class Sane { final status = dylib.sane_open(deviceNamePointer, nativeHandlePointer); print('sane_open() -> ${status.name}'); - _handleSaneStatus(status); + status.check(); final handle = SaneHandle(deviceName: deviceName); _nativeHandles.addAll({ @@ -230,7 +230,7 @@ class Sane { valuePointer = ffi.nullptr; case SaneOptionValueType.group: - throw SaneInvalidException(); + throw SaneInvalidDataException(); } if (action == SaneAction.setValue) { @@ -266,7 +266,7 @@ class Sane { invalid: default: - throw SaneInvalidException(); + throw SaneInvalidDataException(); } } @@ -279,7 +279,7 @@ class Sane { ); print('sane_control_option($index, $action, $value) -> ${status.name}'); - _handleSaneStatus(status); + status.check(); final infos = saneOptionInfoFromNative(infoPointer.value); late final dynamic result; @@ -305,7 +305,7 @@ class Sane { result = null; default: - throw SaneInvalidException(); + throw SaneInvalidDataException(); } ffi.calloc.free(valuePointer); @@ -397,7 +397,7 @@ class Sane { _getNativeHandle(handle), nativeParametersPointer); print('sane_get_parameters() -> ${status.name}'); - _handleSaneStatus(status); + status.check(); final parameters = saneParametersFromNative(nativeParametersPointer.ref); @@ -416,7 +416,7 @@ class Sane { final status = dylib.sane_start(_getNativeHandle(handle)); print('sane_start() -> ${status.name}'); - _handleSaneStatus(status); + status.check(); completer.complete(); }); @@ -439,7 +439,7 @@ class Sane { ); print('sane_read() -> ${status.name}'); - _handleSaneStatus(status); + status.check(); final bytes = Uint8List.fromList( List.generate( @@ -480,37 +480,11 @@ class Sane { ); print('sane_set_io_mode() -> ${status.name}'); - _handleSaneStatus(status); + status.check(); completer.complete(); }); return completer.future; } - - void _handleSaneStatus(SANE_Status status) { - switch (status) { - case SANE_Status.STATUS_UNSUPPORTED: - throw SaneUnsupportedException(); - case SANE_Status.STATUS_CANCELLED: - throw SaneCancelledException(); - case SANE_Status.STATUS_DEVICE_BUSY: - throw SaneDeviceBusyException(); - case SANE_Status.STATUS_INVAL: - throw SaneInvalidException(); - case SANE_Status.STATUS_JAMMED: - throw SaneJammedException(); - case SANE_Status.STATUS_NO_DOCS: - throw SaneNoDocumentsException(); - case SANE_Status.STATUS_COVER_OPEN: - throw SaneCoverOpenException(); - case SANE_Status.STATUS_IO_ERROR: - throw SaneIOErrorException(); - case SANE_Status.STATUS_NO_MEM: - throw SaneNoMemoryException(); - case SANE_Status.STATUS_ACCESS_DENIED: - throw SaneAccessDeniedException(); - default: - } - } } diff --git a/packages/sane/pubspec.lock b/packages/sane/pubspec.lock index 789c73f..bce266d 100644 --- a/packages/sane/pubspec.lock +++ b/packages/sane/pubspec.lock @@ -191,7 +191,7 @@ packages: source: hosted version: "0.12.16+1" meta: - dependency: transitive + dependency: "direct main" description: name: meta sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c diff --git a/packages/sane/pubspec.yaml b/packages/sane/pubspec.yaml index 418b99f..886e81c 100644 --- a/packages/sane/pubspec.yaml +++ b/packages/sane/pubspec.yaml @@ -11,6 +11,7 @@ environment: dependencies: ffi: ^2.1.3 + meta: ^1.16.0 dev_dependencies: ffigen: ^14.0.1