import 'dart:async';

class CancelledException implements Exception {
  final String? reason;
  const CancelledException([this.reason]);
  @override
  String toString() =>
      reason == null ? 'CancelledException' : 'CancelledException: $reason';
}

class CancellationToken {
  final Completer<void> _completer = Completer<void>();
  String? _reason;

  bool get isCancelled => _completer.isCompleted;
  String? get reason => _reason;
  Future<void> get whenCancelled => _completer.future;

  void cancel([String? reason]) {
    if (_completer.isCompleted) return;
    _reason = reason;
    _completer.complete();
  }

  void throwIfCancelled() {
    if (isCancelled) throw CancelledException(_reason);
  }
}
