FileSystemEvent.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. //
  2. // FileSystemEvent.swift
  3. // FileKit
  4. //
  5. // The MIT License (MIT)
  6. //
  7. // Copyright (c) 2015-2017 Nikolai Vazquez
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. //
  27. import Foundation
  28. #if os(OSX)
  29. /// A filesystem event.
  30. public struct FileSystemEvent {
  31. // MARK: - Static Properties
  32. /// All of the event IDs.
  33. public static let allEventId = 0
  34. /// The last event ID since now.
  35. public static let nowEventId = FSEventStreamEventId(kFSEventStreamEventIdSinceNow)
  36. // MARK: - Properties
  37. /// The ID of the event.
  38. public var id: FSEventStreamEventId // swiftlint:disable:this variable_name
  39. /// The path for the event.
  40. public var path: Path
  41. /// The flags of the event.
  42. public var flags: FileSystemEventFlags
  43. }
  44. extension Path {
  45. // MARK: - Watching
  46. /// Watches a path for filesystem events and handles them in the callback.
  47. ///
  48. /// - Parameter latency: The latency in seconds.
  49. /// - Parameter queue: The queue to be run within.
  50. /// - Parameter callback: The callback to handle events.
  51. /// - Returns: The `FileSystemWatcher` object.
  52. public func watch(_ latency: TimeInterval = 0, queue: DispatchQueue = DispatchQueue.main, callback: @escaping (FileSystemEvent) -> Void) -> FileSystemWatcher {
  53. let watcher = FileSystemWatcher(paths: [self], latency: latency, queue: queue, callback: callback)
  54. watcher.watch()
  55. return watcher
  56. }
  57. }
  58. extension Sequence where Self.Iterator.Element == Path {
  59. // MARK: - Watching
  60. /// Watches the sequence of paths for filesystem events and handles them in
  61. /// the callback.
  62. ///
  63. /// - Parameter latency: The latency in seconds.
  64. /// - Parameter queue: The queue to be run within.
  65. /// - Parameter callback: The callback to handle events.
  66. /// - Returns: The `FileSystemWatcher` object.
  67. public func watch(_ latency: TimeInterval = 0, queue: DispatchQueue = DispatchQueue.main, callback: @escaping (FileSystemEvent) -> Void) -> FileSystemWatcher {
  68. let watcher = FileSystemWatcher(paths: Array(self), latency: latency, queue: queue, callback: callback)
  69. watcher.watch()
  70. return watcher
  71. }
  72. }
  73. /// A set of fileystem event flags.
  74. public struct FileSystemEventFlags: OptionSet, CustomStringConvertible, CustomDebugStringConvertible {
  75. // MARK: - Options
  76. /// There was some change in the directory at the specific path supplied in
  77. /// this event.
  78. public static let None = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagNone)
  79. /// Your application must rescan not just the directory given in the event,
  80. /// but all its children, recursively.
  81. public static let MustScanSubDirs = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagMustScanSubDirs)
  82. /// May be set in addition to `MustScanSubDirs` indicate that a problem
  83. /// occurred in buffering the events (the particular flag set indicates
  84. /// where the problem occurred) and that the client must do a full scan of
  85. /// any directories (and their subdirectories, recursively) being monitored
  86. /// by this stream.
  87. public static let UserDropped = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagUserDropped)
  88. /// May be set in addition to `MustScanSubDirs` indicate that a problem
  89. /// occurred in buffering the events (the particular flag set indicates
  90. /// where the problem occurred) and that the client must do a full scan of
  91. /// any directories (and their subdirectories, recursively) being monitored
  92. /// by this stream.
  93. public static let KernelDropped = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagKernelDropped)
  94. /// The 64-bit event ID counter wrapped around.
  95. public static let EventIdsWrapped = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagEventIdsWrapped)
  96. /// Denotes a sentinel event sent to mark the end of the "historical" events
  97. /// sent as a result of specifying a `sinceWhen` value in the
  98. /// FSEventStreamCreate...() call that created this event stream.
  99. public static let HistoryDone = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagHistoryDone)
  100. /// Denotes a special event sent when there is a change to one of the
  101. /// directories along the path to one of the directories asked to watch.
  102. public static let RootChanged = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagRootChanged)
  103. /// Denotes a special event sent when a volume is mounted underneath one of
  104. /// the paths being monitored.
  105. public static let Mount = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagMount)
  106. /// Denotes a special event sent when a volume is unmounted underneath one
  107. /// of the paths being monitored.
  108. public static let Unmount = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagUnmount)
  109. /// A file system object was created at the specific path supplied in this
  110. /// event.
  111. public static let Created = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemCreated)
  112. /// A file system object was removed at the specific path supplied in this
  113. /// event.
  114. public static let ItemRemoved = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemRemoved)
  115. /// A file system object at the specific path supplied in this event had its
  116. /// metadata modified.
  117. public static let ItemInodeMetaMod = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemInodeMetaMod)
  118. /// A file system object was renamed at the specific path supplied in this
  119. /// event.
  120. public static let ItemRenamed = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemRenamed)
  121. /// A file system object at the specific path supplied in this event had its
  122. /// data modified.
  123. public static let ItemModified = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemModified)
  124. /// A file system object at the specific path supplied in this event had its
  125. /// FinderInfo data modified.
  126. public static let ItemFinderInfoMod = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemFinderInfoMod)
  127. /// A file system object at the specific path supplied in this event had its
  128. /// ownership changed.
  129. public static let ItemChangeOwner = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemChangeOwner)
  130. /// A file system object at the specific path supplied in this event had its
  131. /// extended attributes modified.
  132. public static let ItemXattrMod = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemXattrMod)
  133. /// The file system object at the specific path supplied in this event is a
  134. /// regular file.
  135. public static let ItemIsFile = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemIsFile)
  136. /// The file system object at the specific path supplied in this event is a
  137. /// directory.
  138. public static let ItemIsDir = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemIsDir)
  139. /// The file system object at the specific path supplied in this event is a
  140. /// symbolic link.
  141. public static let ItemIsSymlink = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemIsSymlink)
  142. /// Indicates the event was triggered by the current process.
  143. public static let OwnEvent = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagOwnEvent)
  144. /// Flag for if the item is a hardlink.
  145. @available(iOS 9, OSX 10.10, *)
  146. public static let ItemIsHardlink = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemIsHardlink)
  147. /// Flag for if the item was the last hardlink.
  148. @available(iOS 9, OSX 10.10, *)
  149. public static let ItemIsLastHardlink = FileSystemEventFlags(rawValue: kFSEventStreamEventFlagItemIsLastHardlink)
  150. // MARK: - All Flags
  151. /// An array of all of the flags.
  152. public static var allFlags: [FileSystemEventFlags] = {
  153. var array: [FileSystemEventFlags] = [ // swiftlint:disable comma
  154. .None, .MustScanSubDirs, .UserDropped,
  155. .KernelDropped, .EventIdsWrapped, .HistoryDone,
  156. .RootChanged, .Mount, .Unmount,
  157. .ItemRemoved, .ItemInodeMetaMod, .ItemRenamed,
  158. .ItemModified, .ItemFinderInfoMod, .ItemChangeOwner,
  159. .ItemXattrMod, .ItemIsFile, .ItemIsDir,
  160. .ItemIsSymlink, .OwnEvent
  161. ] // swiftlint:enable comma
  162. if #available(iOS 9, OSX 10.10, *) {
  163. array += [.ItemIsHardlink, .ItemIsLastHardlink ]
  164. }
  165. return array
  166. }()
  167. /// The names of all of the flags.
  168. public static let allFlagNames: [String] = {
  169. var array: [String] = [ // swiftlint:disable comma
  170. "None", "MustScanSubDirs", "UserDropped",
  171. "KernelDropped", "EventIdsWrapped", "HistoryDone",
  172. "RootChanged", "Mount", "Unmount",
  173. "ItemRemoved", "ItemInodeMetaMod", "ItemRenamed",
  174. "ItemModified", "ItemFinderInfoMod", "ItemChangeOwner",
  175. "ItemXattrMod", "ItemIsFile", "ItemIsDir",
  176. "ItemIsSymlink", "OwnEvent"
  177. ] // swiftlint:enable comma
  178. if #available(iOS 9, OSX 10.10, *) {
  179. array += ["ItemIsHardlink", "ItemIsLastHardlink"]
  180. }
  181. return array
  182. }()
  183. // MARK: - Properties
  184. /// The raw event stream flag values.
  185. public let rawValue: Int
  186. /// A textual representation of `self`.
  187. public var description: String {
  188. var result = ""
  189. for (index, element) in FileSystemEventFlags.allFlags.enumerated() {
  190. if self.contains(element) {
  191. let name = FileSystemEventFlags.allFlagNames[index]
  192. result += result.isEmpty ? "\(name)": ", \(name)"
  193. }
  194. }
  195. return String(describing: type(of: self)) + "[\(result)]"
  196. }
  197. /// A textual representation of `self`, suitable for debugging.
  198. public var debugDescription: String {
  199. var result = ""
  200. for (index, element) in FileSystemEventFlags.allFlags.enumerated() {
  201. if self.contains(element) {
  202. let name = FileSystemEventFlags.allFlagNames[index] + "(\(element.rawValue))"
  203. result += result.isEmpty ? "\(name)": ", \(name)"
  204. }
  205. }
  206. return String(describing: type(of: self)) + "[\(result)]"
  207. }
  208. // MARK: - Initialization
  209. /// Creates a set of event stream flags from a raw value.
  210. ///
  211. /// - Parameter rawValue: The raw value to initialize from.
  212. public init(rawValue: Int) { self.rawValue = rawValue }
  213. }
  214. /// Flags for creating an event stream.
  215. public struct FileSystemEventStreamCreateFlags: OptionSet, CustomStringConvertible, CustomDebugStringConvertible {
  216. // MARK: - Options
  217. /// The default.
  218. public static let None = FileSystemEventStreamCreateFlags(rawValue: kFSEventStreamCreateFlagNone)
  219. /// The callback function will be invoked with CF types rather than raw C
  220. /// types.
  221. public static let UseCFTypes = FileSystemEventStreamCreateFlags(rawValue: kFSEventStreamCreateFlagUseCFTypes)
  222. /// Affects the meaning of the latency parameter.
  223. public static let FlagNoDefer = FileSystemEventStreamCreateFlags(rawValue: kFSEventStreamCreateFlagNoDefer)
  224. /// Request notifications of changes along the path to the path(s) watched.
  225. public static let WatchRoot = FileSystemEventStreamCreateFlags(rawValue: kFSEventStreamCreateFlagWatchRoot)
  226. /// Don't send events that were triggered by the current process.
  227. public static let IgnoreSelf = FileSystemEventStreamCreateFlags(rawValue: kFSEventStreamCreateFlagIgnoreSelf)
  228. /// Request file-level notifications.
  229. public static let FileEvents = FileSystemEventStreamCreateFlags(rawValue: kFSEventStreamCreateFlagFileEvents)
  230. /// Tag events that were triggered by the current process with the
  231. /// `OwnEvent` flag.
  232. public static let MarkSelf = FileSystemEventStreamCreateFlags(rawValue: kFSEventStreamCreateFlagMarkSelf)
  233. // MARK: - All Flags
  234. /// All of the event stream creation flags.
  235. public static let allFlags: [FileSystemEventStreamCreateFlags] = [.None, .UseCFTypes, .FlagNoDefer, .WatchRoot, .IgnoreSelf, .FileEvents, .MarkSelf]
  236. /// All of the names of the event stream creation flags.
  237. public static let allFlagNames: [String] = ["None", "UseCFTypes", "FlagNoDefer", "WatchRoot", "IgnoreSelf", "FileEvents", "MarkSelf" ]
  238. // MARK: - Properties
  239. /// The raw event stream creation flags.
  240. public let rawValue: Int
  241. /// A textual representation of `self`.
  242. public var description: String {
  243. var result = ""
  244. for (index, element) in FileSystemEventStreamCreateFlags.allFlags.enumerated() {
  245. if self.contains(element) {
  246. let name = FileSystemEventStreamCreateFlags.allFlagNames[index]
  247. result += result.isEmpty ? "\(name)": ", \(name)"
  248. }
  249. }
  250. return String(describing: type(of: self)) + "[\(result)]"
  251. }
  252. /// A textual representation of `self`, suitable for debugging.
  253. public var debugDescription: String {
  254. var result = ""
  255. for (index, element) in FileSystemEventStreamCreateFlags.allFlags.enumerated() {
  256. if self.contains(element) {
  257. let name = FileSystemEventStreamCreateFlags.allFlagNames[index] + "(\(element.rawValue))"
  258. result += result.isEmpty ? "\(name)": ", \(name)"
  259. }
  260. }
  261. return String(describing: type(of: self)) + "[\(result)]"
  262. }
  263. // MARK: - Initialization
  264. /// Creates a set of event stream creation flags from a raw value.
  265. ///
  266. /// - Parameter rawValue: The raw value to initialize from.
  267. public init(rawValue: Int) { self.rawValue = rawValue }
  268. }
  269. #endif