From 77f62396e02bdbecb9ecbb58c3a8729ac82999c8 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 9 Feb 2026 16:57:53 +0100 Subject: [PATCH] Error handling --- CLAUDE.md | 8 +++++ lib/ai/formula_screen.dart | 12 +++----- lib/corpus.dart | 8 ++--- lib/d4rt_formulas.dart | 3 +- lib/error_handler.dart | 44 ++++++++++++++++++++++++++ lib/formula_evaluator.dart | 4 +-- test/error_handler_test.dart | 60 ++++++++++++++++++++++++++++++++++++ 7 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 lib/error_handler.dart create mode 100644 test/error_handler_test.dart diff --git a/CLAUDE.md b/CLAUDE.md index 00d0338..de0cfce 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,3 +11,11 @@ - See `./Makefile` for more examples. +# Workflow +- One feature at a time +- Create a git branch for each new feature +- After making changes, and before consider the feature is implemented + - Allways pass all the tests and integration tests + - Build the application for linux and web-server + - Launch the apllication for web-server, with a timeout of 60s +- Dont merge the feature branch into master, the work will be reviewed by a human. diff --git a/lib/ai/formula_screen.dart b/lib/ai/formula_screen.dart index 17361f3..24e5e89 100644 --- a/lib/ai/formula_screen.dart +++ b/lib/ai/formula_screen.dart @@ -6,6 +6,7 @@ import 'package:markdown/markdown.dart' as markdown; import '../formula_models.dart'; import '../formula_evaluator.dart'; import '../corpus.dart'; +import '../error_handler.dart'; import 'unit_dropdown.dart'; class FormulaScreen extends StatefulWidget { @@ -36,9 +37,8 @@ class D4rtEditingController extends TextEditingController { _lastError = null; return true; } catch (e, s) { + errorHandler.notify(e, s); _lastError = e.toString(); - print("validate: $text: $e"); - print("stack: $s"); return false; } } @@ -95,8 +95,6 @@ class _FormulaScreenState extends State { } void _evaluateFormula() { - print( "EVALUATE FORMULA"); - try { final inputValues = {}; for (final input in widget.formula.input) { @@ -159,12 +157,10 @@ class _FormulaScreenState extends State { setState(() {}); } catch (e, stack) { - debugPrint('Formula evaluation error: $e'); - debugPrint('Stack trace: $stack'); - + errorHandler.notify(e, stack); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Error: ${e.toString()}\n${stack.toString()}'), + content: Text('Error: ${e.toString()}'), backgroundColor: Theme.of(context).colorScheme.error, ), ); diff --git a/lib/corpus.dart b/lib/corpus.dart index fd06dc6..8e52e2f 100644 --- a/lib/corpus.dart +++ b/lib/corpus.dart @@ -154,12 +154,8 @@ class Corpus{ return ret; } catch( e2, stack2 ){ - print(completeSourceExpression); - print(e1); - print(stack1); - print(completeSourceStatement); - print(e2); - print(stack2); + errorHandler.notify(e1.toString() + "\n" + completeSourceExpression, stack1); + errorHandler.notify(e2.toString() + "\n" + completeSourceStatement, stack2); throw FormulaEvaluationException( "Evaluation as statement and expression failed" ); } } diff --git a/lib/d4rt_formulas.dart b/lib/d4rt_formulas.dart index a98dae2..b374a31 100644 --- a/lib/d4rt_formulas.dart +++ b/lib/d4rt_formulas.dart @@ -1,8 +1,9 @@ /// A library for working with mathematical formulas using the d4rt interpreter. -/// +/// /// This library provides data models for representing formulas and an evaluator /// for executing them using the d4rt Dart interpreter. library; export 'formula_models.dart'; export 'formula_evaluator.dart'; +export 'error_handler.dart'; diff --git a/lib/error_handler.dart b/lib/error_handler.dart new file mode 100644 index 0000000..8015dd4 --- /dev/null +++ b/lib/error_handler.dart @@ -0,0 +1,44 @@ +/// Centralized error handler that gets notified of every caught exception +class ErrorHandler { + /// Singleton instance of ErrorHandler + static final ErrorHandler _instance = ErrorHandler._internal(); + + factory ErrorHandler() => _instance; + ErrorHandler._internal(); + + /// Callback function to handle errors - can be overridden for custom behavior + void Function(Object error, [StackTrace? stackTrace])? onError; + + /// Notifies the error handler of an exception + void notify(Object error, [StackTrace? stackTrace]) { + // Print the exception to stdout as requested + print('ErrorHandler caught exception:'); + print(error); + + if (stackTrace != null) { + print('Stack trace:'); + print(stackTrace); + } + + // Call the custom error handler if provided + onError?.call(error, stackTrace); + } + + /// Convenience method to wrap code that might throw exceptions + T handleError(T Function() operation, {T? defaultValue}) { + try { + return operation(); + } catch (error, stackTrace) { + notify(error, stackTrace); + + if (defaultValue != null) { + return defaultValue; + } + + rethrow; + } + } +} + +/// Global instance of ErrorHandler for easy access +final ErrorHandler errorHandler = ErrorHandler(); \ No newline at end of file diff --git a/lib/formula_evaluator.dart b/lib/formula_evaluator.dart index 67dc45c..ca2c88b 100644 --- a/lib/formula_evaluator.dart +++ b/lib/formula_evaluator.dart @@ -2,6 +2,7 @@ import 'dart:math' as Math; import 'package:d4rt/d4rt.dart'; import 'formula_models.dart'; +import 'error_handler.dart'; @@ -105,8 +106,7 @@ class FormulaEvaluator { return result; } catch (e, stack) { - print( "Error evaluating formula source:\n$completeSource" ); - print( stack ); + errorHandler.notify(e.toString() + "\n" + completeSource, stack); throw FormulaEvaluationException( 'Error evaluating formula "${formula.name}": $e', e, diff --git a/test/error_handler_test.dart b/test/error_handler_test.dart new file mode 100644 index 0000000..423ef0b --- /dev/null +++ b/test/error_handler_test.dart @@ -0,0 +1,60 @@ +import 'package:d4rt_formulas/error_handler.dart'; +import 'package:test/test.dart'; + +void main() { + group('ErrorHandler', () { + test('should print exceptions to stdout', () { + final errors = []; + final stacks = []; + + // Capture errors instead of printing them + errorHandler.onError = (error, [stackTrace]) { + errors.add(error); + stacks.add(stackTrace); + }; + + // Simulate an exception being caught + try { + throw Exception('Test exception'); + } catch (e, s) { + errorHandler.notify(e, s); + } + + expect(errors.length, 1); + expect(errors.first.toString(), 'Exception: Test exception'); + expect(stacks.length, 1); + }); + + test('should handle errors in handleError method', () { + final errors = []; + errorHandler.onError = (error, [stackTrace]) { + errors.add(error); + }; + + int result = errorHandler.handleError(() => 42, defaultValue: 0); + expect(result, 42); + + result = errorHandler.handleError(() { + throw Exception('Handled exception'); + }, defaultValue: 100); + + expect(result, 100); + expect(errors.length, 1); + }); + + test('should rethrow exceptions when no default value provided', () { + final errors = []; + errorHandler.onError = (error, [stackTrace]) { + errors.add(error); + }; + + expect(() { + errorHandler.handleError(() { + throw Exception('Rethrown exception'); + }); + }, throwsA(const TypeMatcher())); + + expect(errors.length, 1); + }); + }); +} \ No newline at end of file