@@ -66,7 +66,8 @@ class BetterCommandRunner<O extends OptionDefinition, T>
6666 final MessageOutput ? _messageOutput;
6767 final SetLogLevel ? _setLogLevel;
6868 final OnBeforeRunCommand ? _onBeforeRunCommand;
69- OnAnalyticsEvent ? _onAnalyticsEvent;
69+ final OnAnalyticsEvent ? _onAnalyticsEvent;
70+ bool _analyticsEnabled;
7071
7172 /// The environment variables used for configuration resolution.
7273 final Map <String , String > envVariables;
@@ -157,6 +158,7 @@ class BetterCommandRunner<O extends OptionDefinition, T>
157158 _setLogLevel = setLogLevel,
158159 _onBeforeRunCommand = onBeforeRunCommand,
159160 _onAnalyticsEvent = onAnalyticsEvent,
161+ _analyticsEnabled = onAnalyticsEvent != null ,
160162 envVariables = env ?? Platform .environment,
161163 super (
162164 usageLineLength: wrapTextColumn,
@@ -167,7 +169,7 @@ class BetterCommandRunner<O extends OptionDefinition, T>
167169 _globalOptions = < O > [
168170 StandardGlobalOption .quiet as O ,
169171 StandardGlobalOption .verbose as O ,
170- if (_onAnalyticsEvent != null ) StandardGlobalOption .analytics as O ,
172+ if (this .onAnalyticsEvent != null ) StandardGlobalOption .analytics as O ,
171173 ];
172174 } else {
173175 throw ArgumentError (
@@ -198,7 +200,50 @@ class BetterCommandRunner<O extends OptionDefinition, T>
198200 }
199201
200202 /// Checks if analytics is enabled.
201- bool analyticsEnabled () => _onAnalyticsEvent != null ;
203+ /// Note that the return value may change after the [run] method has started.
204+ /// Can be overridden.
205+ bool analyticsEnabled () => _analyticsEnabled;
206+
207+ /// Gets the [onAnalyticsEvent] callback, if set.
208+ OnAnalyticsEvent ? get onAnalyticsEvent => _onAnalyticsEvent;
209+
210+ /// Sends an analytics event, provided the analytics are enabled.
211+ /// Invoked from BetterCommandRunner upon command execution
212+ /// with the event name, or command name if applicable.
213+ /// Can be overridden to customize the event sending behavior.
214+ void sendAnalyticsEvent (final String event) {
215+ if (analyticsEnabled ()) {
216+ try {
217+ onAnalyticsEvent? .call (event);
218+ } catch (_) {
219+ // Silently ignore analytics sending errors to not disrupt the main flow
220+ }
221+ }
222+ }
223+
224+ /// Determines the analytics settings based on configuration / settings.
225+ /// Called from [run] before any analytics events are sent and before any
226+ /// command is run.
227+ ///
228+ /// [globalConfiguration] is set before this method is called.
229+ ///
230+ /// By default it checks whether the [onAnalyticsEvent] callback is set
231+ /// and the `--analytics` option.
232+ /// Subclasses can override this method to customize the behavior,
233+ /// e.g. to ask the user for permission.
234+ Future <bool > determineAnalyticsSettings () async {
235+ if (onAnalyticsEvent == null ) {
236+ return false ;
237+ }
238+
239+ if (globalConfiguration.findValueOf (
240+ argName: BetterCommandRunnerFlags .analytics) ==
241+ false ) {
242+ return false ;
243+ }
244+
245+ return true ;
246+ }
202247
203248 /// Parses [args] and invokes [Command.run] on the chosen command.
204249 ///
@@ -211,11 +256,13 @@ class BetterCommandRunner<O extends OptionDefinition, T>
211256 /// the global configuration is set, see [globalConfiguration] .
212257 @override
213258 Future <T ?> run (final Iterable <String > args) {
214- return Future .sync (() {
259+ return Future .sync (() async {
215260 final argResults = parse (args);
216261 globalConfiguration = resolveConfiguration (argResults);
217262
218263 try {
264+ _analyticsEnabled = await determineAnalyticsSettings ();
265+
219266 if (globalConfiguration.errors.isNotEmpty) {
220267 final buffer = StringBuffer ();
221268 final errors = globalConfiguration.errors.map (formatConfigError);
@@ -224,7 +271,7 @@ class BetterCommandRunner<O extends OptionDefinition, T>
224271 }
225272 } on UsageException catch (e) {
226273 messageOutput? .logUsageException (e);
227- _onAnalyticsEvent ? . call (BetterCommandRunnerAnalyticsEvents .invalid);
274+ sendAnalyticsEvent (BetterCommandRunnerAnalyticsEvents .invalid);
228275 rethrow ;
229276 }
230277
@@ -239,7 +286,7 @@ class BetterCommandRunner<O extends OptionDefinition, T>
239286 return super .parse (args);
240287 } on UsageException catch (e) {
241288 messageOutput? .logUsageException (e);
242- _onAnalyticsEvent ? . call (BetterCommandRunnerAnalyticsEvents .invalid);
289+ sendAnalyticsEvent (BetterCommandRunnerAnalyticsEvents .invalid);
243290 rethrow ;
244291 }
245292 }
@@ -268,21 +315,15 @@ class BetterCommandRunner<O extends OptionDefinition, T>
268315 commandName: topLevelResults.command? .name,
269316 );
270317
271- if (globalConfiguration.findValueOf (
272- argName: BetterCommandRunnerFlags .analytics) ==
273- false ) {
274- _onAnalyticsEvent = null ;
275- }
276-
277318 unawaited (
278- Future (() async {
319+ Future . sync (() {
279320 final command = topLevelResults.command;
280321 if (command != null ) {
281322 // Command name can only be null for top level results.
282323 // But since we are taking the name of a command from the top level
283324 // results there should always be a name specified.
284325 assert (command.name != null , 'Command name should never be null.' );
285- _onAnalyticsEvent ? . call (
326+ sendAnalyticsEvent (
286327 command.name ?? BetterCommandRunnerAnalyticsEvents .invalid,
287328 );
288329 return ;
@@ -297,7 +338,7 @@ class BetterCommandRunner<O extends OptionDefinition, T>
297338 // so the try/catch statement can't be fully compensated for handled here.
298339 final noUnexpectedArgs = topLevelResults.rest.isEmpty;
299340 if (noUnexpectedArgs) {
300- _onAnalyticsEvent ? . call (BetterCommandRunnerAnalyticsEvents .help);
341+ sendAnalyticsEvent (BetterCommandRunnerAnalyticsEvents .help);
301342 }
302343 }),
303344 );
@@ -308,7 +349,7 @@ class BetterCommandRunner<O extends OptionDefinition, T>
308349 return await super .runCommand (topLevelResults);
309350 } on UsageException catch (e) {
310351 messageOutput? .logUsageException (e);
311- _onAnalyticsEvent ? . call (BetterCommandRunnerAnalyticsEvents .invalid);
352+ sendAnalyticsEvent (BetterCommandRunnerAnalyticsEvents .invalid);
312353 rethrow ;
313354 }
314355 }
0 commit comments