From 34ac2ae201e8547349f7f77ff9972337d999841c Mon Sep 17 00:00:00 2001
From: Chen Asraf <casraf@pm.me>
Date: Mon, 22 Jul 2024 01:40:47 +0300
Subject: [PATCH] chore: improve error logging

---
 bin/script_runner.dart | 15 +++++++++------
 lib/base.dart          | 20 +++++++++++++-------
 lib/utils.dart         | 15 +++++++++++++--
 pubspec.yaml           | 17 ++++++++---------
 4 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/bin/script_runner.dart b/bin/script_runner.dart
index f05f257..df97a61 100755
--- a/bin/script_runner.dart
+++ b/bin/script_runner.dart
@@ -6,7 +6,8 @@ import 'package:script_runner/utils.dart';
 /// Main entrypoint for CMD script runner.
 Future<void> main(List<String> args) async {
   if (args.isEmpty) {
-    printColor('No script command provided. Use -h to see available commands.', [TerminalColor.red]);
+    printColor('No script command provided. Use -h or -ls to see available commands.',
+        [TerminalColor.red]);
     return;
   }
   final scriptCmd = args.first;
@@ -15,13 +16,15 @@ Future<void> main(List<String> args) async {
     final code = await runScript(scriptCmd, scriptArgs);
     io.exit(code);
   } catch (e, stack) {
-    if (e is ScriptStateError) {
+    if (e is ScriptError) {
       printColor(e.toString(), [TerminalColor.red]);
-    } else {
-      printColor('$e\n$stack', [TerminalColor.red]);
-    }
-    if (e is io.ProcessException) {
+    } else if (e is io.ProcessException) {
+      printColor('Error in script "$scriptCmd": ${e.message}', [TerminalColor.red]);
       io.exit(e.errorCode);
+    } else {
+      printColor('Error executing script: $e\n$stack', [TerminalColor.red]);
+      io.exit(1);
     }
   }
 }
+
diff --git a/lib/base.dart b/lib/base.dart
index 819b346..54d4918 100644
--- a/lib/base.dart
+++ b/lib/base.dart
@@ -7,7 +7,7 @@ Future<int> runScript(String entryName, List<String> args) async {
   final config = await ScriptRunnerConfig.get();
 
   if (config.scripts.isEmpty) {
-    throw ScriptStateError('No scripts found');
+    throw ScriptNotFoundError('No scripts found');
   }
 
   if (['-h', '--help'].contains(entryName)) {
@@ -24,27 +24,33 @@ Future<int> runScript(String entryName, List<String> args) async {
   final entry = config.scriptsMap[entryName];
 
   if (entry == null) {
-    final suggestions =
-        config.scriptsMap.keys.where((key) => key.toLowerCase().startsWith(entryName.toLowerCase())).toList();
+    final suggestions = config.scriptsMap.keys
+        .where((key) => key.toLowerCase().startsWith(entryName.toLowerCase()))
+        .toList();
 
     if (suggestions.isNotEmpty) {
       if (suggestions.length == 1) {
-        throw ScriptStateError(
+        throw ScriptNotFoundError(
           'No script named "$entryName" found. Did you mean "${suggestions.single}"?',
         );
       } else {
-        throw ScriptStateError(
+        throw ScriptNotFoundError(
           'No script named "$entryName" found.\n'
           'Did you mean one of: "${suggestions.join('", "')}"?',
         );
       }
     } else {
-      throw ScriptStateError(
+      throw ScriptNotFoundError(
         'No script named "$entryName" found.\n'
         'Available scripts: ${config.scriptsMap.keys.join('", "')}',
       );
     }
   }
 
-  return entry.run(args);
+  try {
+    return entry.run(args);
+  } catch (e, stack) {
+    throw ScriptError('Error running script "$entryName": $e\n$stack');
+  }
 }
+
diff --git a/lib/utils.dart b/lib/utils.dart
index 93f60c4..01a4eac 100644
--- a/lib/utils.dart
+++ b/lib/utils.dart
@@ -135,9 +135,20 @@ class TerminalColor {
   static const TerminalColor underline = TerminalColor._(4);
 }
 
-class ScriptStateError extends StateError {
-  ScriptStateError(super.message);
+/// An error that occurs that is related to a script.
+class ScriptError extends StateError {
+  ScriptError(super.message);
 
   @override
   String toString() => message;
 }
+
+/// An error that occurs during script execution.
+class ScriptStateError extends ScriptError {
+  ScriptStateError(super.message);
+}
+
+/// An error that occurs when a script is not found.
+class ScriptNotFoundError extends ScriptError {
+  ScriptNotFoundError(super.message);
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index aabd989..7f280f5 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -20,11 +20,12 @@ dev_dependencies:
   btool: any
 
 script_runner:
-  # line_length: 100
   scripts:
+    # ================================================================================
     # Real
+    # ================================================================================
     - auto-fix: dart fix --apply
-    - publish: dart format .; dart pub publish; format
+    - publish: dart format .; dart pub publish
     - publish:dry: dart pub publish --dry-run
     - doc: dart doc
     - name: version
@@ -33,15 +34,13 @@ script_runner:
     - name: 'version:set'
       cmd: dart run btool set packageVersion
       display_cmd: false
-    - format: dart format --line-length 120 .
-    - name: clean
-      cmd: rm -rf .dart_tool/pub/bin/script_runner/script_runner.dart-*.snapshot
-    - name: activate-local
-      cmd: scr clean && dart pub global deactivate script_runner; dart pub global activate --source path ./
-    - name: activate-global
-      cmd: scr clean && dart pub global deactivate script_runner; dart pub global activate script_runner
+    - clean: rm -rf .dart_tool/pub/bin/script_runner/script_runner.dart-*.snapshot
+    - activate-local: scr clean && dart pub global deactivate script_runner; dart pub global activate --source path ./
+    - activate-global: scr clean && dart pub global deactivate script_runner; dart pub global activate script_runner
 
+    # ================================================================================
     # Examples
+    # ================================================================================
     - name: echo1
       cmd: echo "Hello World" $SHELL
       description: Interdum a scelerisque arcu felis taciti ligula pellentesque curabitur, suspendisse adipiscing quisque sed luctus elementum in imperdiet id, praesent enim sem justo sapien diam nec. Quisque erat risus sagittis penatibus per, vehicula sociosqu cubilia convallis, sollicitudin scelerisque cras aptent. Natoque ornare dictumst netus litora mollis suspendisse cubilia proin morbi primis consequat eu massa, cursus non urna ridiculus dolor duis tempus ut nam velit lacus viverra. A interdum senectus eu mus leo aptent facilisi augue tristique ante purus condimentum pulvinar porta viverra morbi, et tellus gravida porttitor non euismod suscipit neque egestas praesent arcu luctus pharetra fusce. Luctus mauris a venenatis tempus cras ante efficitur massa ultricies mollis lacus, volutpat nisi lacinia himenaeos facilisi in aliquet sodales purus integer vitae quisque, libero torquent enim mattis placerat tortor mi dignissim viverra sem.