Operators.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. //
  2. // Operators.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. // swiftlint:disable file_length
  28. import Foundation
  29. private func < <T: Comparable>(lhs: T?, rhs: T?) -> Bool {
  30. switch (lhs, rhs) {
  31. case let (l?, r?):
  32. return l < r
  33. case (nil, _?):
  34. return true
  35. default:
  36. return false
  37. }
  38. }
  39. // MARK: - File
  40. /// Returns `true` if both files' paths are the same.
  41. public func ==<DataType>(lhs: File<DataType>, rhs: File<DataType>) -> Bool {
  42. return lhs.path == rhs.path
  43. }
  44. /// Returns `true` if `lhs` is smaller than `rhs` in size.
  45. public func < <DataType>(lhs: File<DataType>, rhs: File<DataType>) -> Bool {
  46. return lhs.size < rhs.size
  47. }
  48. infix operator |>
  49. /// Writes data to a file.
  50. ///
  51. /// - Throws: `FileKitError.WriteToFileFail`
  52. ///
  53. public func |> <DataType>(data: DataType, file: File<DataType>) throws {
  54. try file.write(data)
  55. }
  56. // MARK: - TextFile
  57. /// Returns `true` if both text files have the same path and encoding.
  58. public func == (lhs: TextFile, rhs: TextFile) -> Bool {
  59. return lhs.path == rhs.path && lhs.encoding == rhs.encoding
  60. }
  61. infix operator |>>
  62. /// Appends a string to a text file.
  63. ///
  64. /// If the text file can't be read from, such in the case that it doesn't exist,
  65. /// then it will try to write the data directly to the file.
  66. ///
  67. /// - Throws: `FileKitError.WriteToFileFail`
  68. ///
  69. public func |>> (data: String, file: TextFile) throws {
  70. let textStreamWriter = try file.streamWriter(append: true)
  71. guard textStreamWriter.writeDelimiter(), textStreamWriter.write(line: data, delim: false) else {
  72. let reason: FileKitError.ReasonError
  73. if textStreamWriter.isClosed {
  74. reason = .closed
  75. } else {
  76. reason = .encoding(textStreamWriter.encoding, data: data)
  77. }
  78. throw FileKitError.writeToFileFail(path: file.path, error: reason)
  79. }
  80. }
  81. /// Return lines of file that match the motif.
  82. public func | (file: TextFile, motif: String) -> [String] {
  83. return file.grep(motif)
  84. }
  85. infix operator |-
  86. /// Return lines of file that does'nt match the motif.
  87. public func |- (file: TextFile, motif: String) -> [String] {
  88. return file.grep(motif, include: false)
  89. }
  90. infix operator |~
  91. /// Return lines of file that match the regex motif.
  92. public func |~ (file: TextFile, motif: String) -> [String] {
  93. return file.grep(motif, options: .regularExpression)
  94. }
  95. // MARK: - Path
  96. /// Returns `true` if the standardized form of one path equals that of another
  97. /// path.
  98. public func == (lhs: Path, rhs: Path) -> Bool {
  99. if lhs.isAbsolute || rhs.isAbsolute {
  100. return lhs.absolute.rawValue == rhs.absolute.rawValue
  101. }
  102. return lhs.standardRawValueWithTilde == rhs.standardRawValueWithTilde
  103. }
  104. /// Returns `true` if the standardized form of one path not equals that of another
  105. /// path.
  106. public func != (lhs: Path, rhs: Path) -> Bool {
  107. return !(lhs == rhs)
  108. }
  109. /// Concatenates two `Path` instances and returns the result.
  110. ///
  111. /// ```swift
  112. /// let systemLibrary: Path = "/System/Library"
  113. /// print(systemLib + "Fonts") // "/System/Library/Fonts"
  114. /// ```
  115. ///
  116. public func + (lhs: Path, rhs: Path) -> Path {
  117. if lhs.rawValue.isEmpty || lhs.rawValue == "." { return rhs }
  118. if rhs.rawValue.isEmpty || rhs.rawValue == "." { return lhs }
  119. switch (lhs.rawValue.hasSuffix(Path.separator), rhs.rawValue.hasPrefix(Path.separator)) {
  120. case (true, true):
  121. let rhsRawValue = rhs.dropFirst()
  122. return Path("\(lhs.rawValue)\(rhsRawValue)")
  123. case (false, false):
  124. return Path("\(lhs.rawValue)\(Path.separator)\(rhs.rawValue)")
  125. default:
  126. return Path("\(lhs.rawValue)\(rhs.rawValue)")
  127. }
  128. }
  129. /// Converts a `String` to a `Path` and returns the concatenated result.
  130. public func + (lhs: String, rhs: Path) -> Path {
  131. return Path(lhs) + rhs
  132. }
  133. /// Converts a `String` to a `Path` and returns the concatenated result.
  134. public func + (lhs: Path, rhs: String) -> Path {
  135. return lhs + Path(rhs)
  136. }
  137. /// Appends the right path to the left path.
  138. public func += (lhs: inout Path, rhs: Path) {
  139. // swiftlint:disable:next shorthand_operator
  140. lhs = lhs + rhs
  141. }
  142. /// Appends the path value of the String to the left path.
  143. public func += (lhs: inout Path, rhs: String) {
  144. // swiftlint:disable:next shorthand_operator
  145. lhs = lhs + rhs
  146. }
  147. /// Concatenates two `Path` instances and returns the result.
  148. public func / (lhs: Path, rhs: Path) -> Path {
  149. return lhs + rhs
  150. }
  151. /// Converts a `String` to a `Path` and returns the concatenated result.
  152. public func / (lhs: Path, rhs: String) -> Path {
  153. return lhs + rhs
  154. }
  155. /// Converts a `String` to a `Path` and returns the concatenated result.
  156. public func / (lhs: String, rhs: Path) -> Path {
  157. return lhs + rhs
  158. }
  159. /// Appends the right path to the left path.
  160. public func /= (lhs: inout Path, rhs: Path) {
  161. lhs += rhs
  162. }
  163. /// Appends the path value of the String to the left path.
  164. public func /= (lhs: inout Path, rhs: String) {
  165. lhs += rhs
  166. }
  167. precedencegroup FileCommonAncestorPrecedence {
  168. associativity: left
  169. }
  170. infix operator <^> : FileCommonAncestorPrecedence
  171. /// Returns the common ancestor between the two paths.
  172. public func <^> (lhs: Path, rhs: Path) -> Path {
  173. return lhs.commonAncestor(rhs)
  174. }
  175. infix operator </>
  176. /// Runs `closure` with the path as its current working directory.
  177. public func </> (path: Path, closure: () throws -> Void) rethrows {
  178. try path.changeDirectory(closure)
  179. }
  180. infix operator ->>
  181. /// Moves the file at the left path to a path.
  182. ///
  183. /// Throws an error if the file at the left path could not be moved or if a file
  184. /// already exists at the right path.
  185. ///
  186. /// - Throws: `FileKitError.FileDoesNotExist`, `FileKitError.MoveFileFail`
  187. ///
  188. public func ->> (lhs: Path, rhs: Path) throws {
  189. try lhs.moveFile(to: rhs)
  190. }
  191. /// Moves a file to a path.
  192. ///
  193. /// Throws an error if the file could not be moved or if a file already
  194. /// exists at the destination path.
  195. ///
  196. /// - Throws: `FileKitError.FileDoesNotExist`, `FileKitError.MoveFileFail`
  197. ///
  198. public func ->> <DataType>(lhs: File<DataType>, rhs: Path) throws {
  199. try lhs.move(to: rhs)
  200. }
  201. infix operator ->!
  202. /// Forcibly moves the file at the left path to the right path.
  203. ///
  204. /// - Warning: If a file at the right path already exists, it will be deleted.
  205. ///
  206. /// - Throws:
  207. /// `FileKitError.DeleteFileFail`,
  208. /// `FileKitError.FileDoesNotExist`,
  209. /// `FileKitError.CreateSymlinkFail`
  210. ///
  211. public func ->! (lhs: Path, rhs: Path) throws {
  212. if rhs.isAny {
  213. try rhs.deleteFile()
  214. }
  215. try lhs ->> rhs
  216. }
  217. /// Forcibly moves a file to a path.
  218. ///
  219. /// - Warning: If a file at the right path already exists, it will be deleted.
  220. ///
  221. /// - Throws:
  222. /// `FileKitError.DeleteFileFail`,
  223. /// `FileKitError.FileDoesNotExist`,
  224. /// `FileKitError.CreateSymlinkFail`
  225. ///
  226. public func ->! <DataType>(lhs: File<DataType>, rhs: Path) throws {
  227. if rhs.isAny {
  228. try rhs.deleteFile()
  229. }
  230. try lhs ->> rhs
  231. }
  232. infix operator +>>
  233. /// Copies the file at the left path to the right path.
  234. ///
  235. /// Throws an error if the file at the left path could not be copied or if a file
  236. /// already exists at the right path.
  237. ///
  238. /// - Throws: `FileKitError.FileDoesNotExist`, `FileKitError.CopyFileFail`
  239. ///
  240. public func +>> (lhs: Path, rhs: Path) throws {
  241. try lhs.copyFile(to: rhs)
  242. }
  243. /// Copies a file to a path.
  244. ///
  245. /// Throws an error if the file could not be copied or if a file already
  246. /// exists at the destination path.
  247. ///
  248. /// - Throws: `FileKitError.FileDoesNotExist`, `FileKitError.CopyFileFail`
  249. ///
  250. public func +>> <DataType>(lhs: File<DataType>, rhs: Path) throws {
  251. try lhs.copy(to: rhs)
  252. }
  253. infix operator +>!
  254. /// Forcibly copies the file at the left path to the right path.
  255. ///
  256. /// - Warning: If a file at the right path already exists, it will be deleted.
  257. ///
  258. /// - Throws:
  259. /// `FileKitError.DeleteFileFail`,
  260. /// `FileKitError.FileDoesNotExist`,
  261. /// `FileKitError.CreateSymlinkFail`
  262. ///
  263. public func +>! (lhs: Path, rhs: Path) throws {
  264. if rhs.isAny {
  265. try rhs.deleteFile()
  266. }
  267. try lhs +>> rhs
  268. }
  269. /// Forcibly copies a file to a path.
  270. ///
  271. /// - Warning: If a file at the right path already exists, it will be deleted.
  272. ///
  273. /// - Throws:
  274. /// `FileKitError.DeleteFileFail`,
  275. /// `FileKitError.FileDoesNotExist`,
  276. /// `FileKitError.CreateSymlinkFail`
  277. ///
  278. public func +>! <DataType>(lhs: File<DataType>, rhs: Path) throws {
  279. if rhs.isAny {
  280. try rhs.deleteFile()
  281. }
  282. try lhs +>> rhs
  283. }
  284. infix operator =>>
  285. /// Creates a symlink of the left path at the right path.
  286. ///
  287. /// If the symbolic link path already exists and _is not_ a directory, an
  288. /// error will be thrown and a link will not be created.
  289. ///
  290. /// If the symbolic link path already exists and _is_ a directory, the link
  291. /// will be made to a file in that directory.
  292. ///
  293. /// - Throws:
  294. /// `FileKitError.FileDoesNotExist`,
  295. /// `FileKitError.CreateSymlinkFail`
  296. ///
  297. public func =>> (lhs: Path, rhs: Path) throws {
  298. try lhs.symlinkFile(to: rhs)
  299. }
  300. /// Symlinks a file to a path.
  301. ///
  302. /// If the path already exists and _is not_ a directory, an error will be
  303. /// thrown and a link will not be created.
  304. ///
  305. /// If the path already exists and _is_ a directory, the link will be made
  306. /// to the file in that directory.
  307. ///
  308. /// - Throws: `FileKitError.FileDoesNotExist`, `FileKitError.CreateSymlinkFail`
  309. ///
  310. public func =>> <DataType>(lhs: File<DataType>, rhs: Path) throws {
  311. try lhs.symlink(to: rhs)
  312. }
  313. infix operator =>!
  314. /// Forcibly creates a symlink of the left path at the right path by deleting
  315. /// anything at the right path before creating the symlink.
  316. ///
  317. /// - Warning: If the symbolic link path already exists, it will be deleted.
  318. ///
  319. /// - Throws:
  320. /// `FileKitError.DeleteFileFail`,
  321. /// `FileKitError.FileDoesNotExist`,
  322. /// `FileKitError.CreateSymlinkFail`
  323. ///
  324. public func =>! (lhs: Path, rhs: Path) throws {
  325. // guard lhs.exists else {
  326. // throw FileKitError.FileDoesNotExist(path: lhs)
  327. // }
  328. let linkPath = rhs.isDirectory ? rhs + lhs.fileName : rhs
  329. if linkPath.isAny { try linkPath.deleteFile() }
  330. try lhs =>> rhs
  331. }
  332. /// Forcibly creates a symlink of a file at a path by deleting anything at the
  333. /// path before creating the symlink.
  334. ///
  335. /// - Warning: If the path already exists, it will be deleted.
  336. ///
  337. /// - Throws:
  338. /// `FileKitError.DeleteFileFail`,
  339. /// `FileKitError.FileDoesNotExist`,
  340. /// `FileKitError.CreateSymlinkFail`
  341. ///
  342. public func =>! <DataType>(lhs: File<DataType>, rhs: Path) throws {
  343. try lhs.path =>! rhs
  344. }
  345. postfix operator %
  346. /// Returns the standardized version of the path.
  347. public postfix func % (path: Path) -> Path {
  348. return path.standardized
  349. }
  350. postfix operator *
  351. /// Returns the resolved version of the path.
  352. public postfix func * (path: Path) -> Path {
  353. return path.resolved
  354. }
  355. postfix operator ^
  356. /// Returns the path's parent path.
  357. public postfix func ^ (path: Path) -> Path {
  358. return path.parent
  359. }