| // Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:io'; |
| |
| /// Extension type replacing [FileSystemEvent] for `package:watcher` internal |
| /// use. |
| /// |
| /// The [FileSystemDeleteEvent] subclass of [FileSystemEvent] does something |
| /// surprising for `isDirectory`: it always returns `false`. The constructor |
| /// accepts a boolean called `isDirectory` but discards it. |
| /// |
| /// So, this extension type hides `isDirectory` and instead provides an |
| /// [EventType] enum with the seven types of event actually used. |
| extension type Event._(FileSystemEvent _event) { |
| /// Converts [event] to an [Event]. |
| /// |
| /// Returns `null` and asserts `false` if [event] is unexpected on this |
| /// platform. So, it will cause tests to fail but real code can continue |
| /// ignoring the event. |
| /// |
| /// Returns `null` if [event] should be ignored on this platform. |
| static Event? checkAndConvert(FileSystemEvent event) { |
| var result = Event._(event); |
| if (result.type.isIgnored) return null; |
| if (Platform.isMacOS && result.type.isNeverReceivedOnMacOS) { |
| assert(false); |
| return null; |
| } |
| return result; |
| } |
| |
| /// Returns an iterable containing this event, split to a "create" and a |
| /// "delete" event if it's a move event. |
| Iterable<Event> splitIfMove() sync* { |
| if (type != EventType.moveFile && type != EventType.moveDirectory) { |
| yield this; |
| return; |
| } |
| final destination = this.destination; |
| yield Event._(FileSystemDeleteEvent(path, type == EventType.moveDirectory)); |
| if (destination != null) { |
| yield Event._( |
| FileSystemCreateEvent(destination, type == EventType.moveDirectory), |
| ); |
| } |
| } |
| |
| /// A create event for a file at [path]. |
| static Event createFile(String path) => |
| Event._(FileSystemCreateEvent(path, false)); |
| |
| /// A create event for a directory at [path]. |
| static Event createDirectory(String path) => |
| Event._(FileSystemCreateEvent(path, true)); |
| |
| /// A delete event for [path]. |
| /// |
| /// Delete events do not specify whether they are for files or directories. |
| static Event delete(String path) => Event._( |
| FileSystemDeleteEvent( |
| path, |
| // `FileSystemDeleteEvent` just discards `isDirectory`. |
| false /* isDirectory */, |
| ), |
| ); |
| |
| /// A modify event for the file at [path]. |
| static Event modifyFile(String path) => Event._( |
| FileSystemModifyEvent( |
| path, |
| false /* isDirectory */, |
| // Don't set `contentChanged`, even pass through from the OS, as |
| // `package:watcher` never reads it. |
| false /* contentChanged */, |
| ), |
| ); |
| |
| /// See [FileSystemEvent.path]. |
| String get path => _event.path; |
| |
| EventType get type { |
| switch (_event.type) { |
| case FileSystemEvent.create: |
| return _event.isDirectory |
| ? EventType.createDirectory |
| : EventType.createFile; |
| case FileSystemEvent.delete: |
| return EventType.delete; |
| case FileSystemEvent.modify: |
| return _event.isDirectory |
| ? EventType.modifyDirectory |
| : EventType.modifyFile; |
| case FileSystemEvent.move: |
| return _event.isDirectory |
| ? EventType.moveDirectory |
| : EventType.moveFile; |
| default: |
| throw StateError('Invalid event type ${_event.type}.'); |
| } |
| } |
| |
| /// See [FileSystemMoveEvent.destination]. |
| /// |
| /// For other types of event, always `null`. |
| String? get destination => _event.type == FileSystemEvent.move |
| ? (_event as FileSystemMoveEvent).destination |
| : null; |
| } |
| |
| /// See [FileSystemEvent.type]. |
| /// |
| /// This additionally encodes [FileSystemEvent.isDirectory], which is specified |
| /// for all event types except deletes. |
| enum EventType { |
| delete, |
| createFile, |
| createDirectory, |
| modifyFile, |
| modifyDirectory, |
| moveFile, |
| moveDirectory; |
| |
| bool get isNeverReceivedOnMacOS { |
| // See https://github.com/dart-lang/sdk/issues/14806. |
| return this == moveFile || this == moveDirectory; |
| } |
| |
| bool get isIgnored { |
| // On Windows, `modifyDirectory` is always accompanied by either |
| // `createDirectory` or `deleteDirectory`, so it's not needed. |
| // |
| // On Linux, `modifyDirectory` means the directory attributes such as |
| // permissions changed, so it's not a useful event. |
| // |
| // `modifyDirectory` on Mac also relates to directory attributes but is |
| // harder to repro, using `unzip` works. It's not a useful event. See |
| // https://github.com/dart-lang/tools/issues/2283. |
| return this == modifyDirectory; |
| } |
| } |