1 | | | import 'dart:convert'; |
2 | | |
|
3 | | | import 'squadron_error.dart'; |
4 | | | import 'worker_exception.dart'; |
5 | | |
|
6 | | | typedef WorkerExceptionDeserializer = WorkerException? Function(List data); |
7 | | |
|
8 | | | /// Base abstract class for exceptions in Squadron. |
9 | | | abstract class SquadronException implements Exception { |
10 | | | /// This factory returns [error] if it is a [SquadronException] (enriching it with [workerId] and [command] if it |
11 | | | /// is a [WorkerException]). Otherwise, it returns a new [WorkerException] wrapping [error] and [stackTrace]. |
12 | | 2 | factory SquadronException.from( |
13 | | | {required Object error, |
14 | | | StackTrace? stackTrace, |
15 | | | String? workerId, |
16 | | | int? command}) { |
17 | | 2 | if (error is SquadronError) { |
18 | | 0 | return error; |
19 | | 1 | } else if (error is WorkerException) { |
20 | | 3 | return error.withCommand(command).withWorkerId(workerId); |
21 | | | } else { |
22 | | 3 | return WorkerException(error.toString(), |
23 | | | stackTrace: stackTrace, workerId: workerId, command: command); |
24 | | | } |
25 | | 1 | } |
26 | | |
|
27 | | 1 | static SquadronException? fromString(String message) { |
28 | | | try { |
29 | | 1 | final data = jsonDecode(message); |
30 | | 1 | if (data is List) { |
31 | | 1 | return deserialize(data); |
32 | | | } |
33 | | | } catch (ex) { |
34 | | | // not a String representing a SquadronException |
35 | | | } |
36 | | | return null; |
37 | | | } |
38 | | |
|
39 | | 0 | @override |
40 | | 0 | String toString() => jsonEncode(serialize()); |
41 | | |
|
42 | | | /// The exception's [StackTrace]. |
43 | | | StackTrace? get stackTrace; |
44 | | |
|
45 | | | /// Serializes the exception, i.e. returns a list of items that can cross thread boundaries. |
46 | | | List serialize(); |
47 | | |
|
48 | | 3 | static final _customDeserializers = <WorkerExceptionDeserializer>[]; |
49 | | |
|
50 | | | /// Registers the deserializer for a custom [WorkerException]. |
51 | | 1 | static void registerExceptionDeserializer( |
52 | | | WorkerExceptionDeserializer deserializer) { |
53 | | 3 | _customDeserializers.add(deserializer); |
54 | | | } |
55 | | |
|
56 | | | /// Deserializes a [stackTrace] if any. Ruturns null if no [StackTrace] is provided. |
57 | | 1 | static StackTrace? loadStackTrace(String? stackTrace) => |
58 | | 1 | (stackTrace == null) ? null : StackTrace.fromString(stackTrace); |
59 | | |
|
60 | | | /// Deserializes a [List] that was produced by [serialize]. |
61 | | 2 | static SquadronException? deserialize(List? data) { |
62 | | 1 | if (data == null) { |
63 | | 1 | return null; |
64 | | | } |
65 | | | SquadronException? error; |
66 | | 0 | try { |
67 | | 2 | error = SquadronError.deserialize(data) ?? |
68 | | 1 | WorkerException.deserialize(data) ?? |
69 | | 1 | CancelledException.deserialize(data) ?? |
70 | | 1 | TaskTimeoutException.deserialize(data); |
71 | | 1 | if (error == null) { |
72 | | 4 | for (var i = 0; i < _customDeserializers.length; i++) { |
73 | | 3 | final deserializer = _customDeserializers[i]; |
74 | | 2 | error = deserializer(data); |
75 | | 1 | if (error != null) { |
76 | | 1 | break; |
77 | | | } |
78 | | | } |
79 | | | } |
80 | | 0 | error ??= newSquadronError( |
81 | | 0 | 'failed to deserialize exception information: $data'); |
82 | | | } catch (ex) { |
83 | | | error = |
84 | | 0 | newSquadronError('failed to deserialize exception information: $ex'); |
85 | | | } |
86 | | 1 | return error; |
87 | | 1 | } |
88 | | | } |