diff --git a/.travis.yml b/.travis.yml index 61e0dca..598f1fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ xcode_scheme: Path.swift-Package jobs: include: - - script: swift test + - script: swift test --parallel name: macOS - &xcodebuild @@ -35,14 +35,19 @@ jobs: -destination 'platform=watchOS Simulator,OS=latest,name=Apple Watch Series 4 - 40mm' \ build | xcpretty - - env: SWIFT_VERSION=4.2.1 + - &linux + env: SWIFT_VERSION=4.2.1 os: linux - name: Linux + name: Linux / Swift 4.2.1 language: generic dist: trusty sudo: false install: eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" - script: swift test + script: swift test --parallel + + - <<: *linux + env: SWIFT_VERSION='5.0-DEVELOPMENT-SNAPSHOT-2019-01-22-a' + name: Linux / Swift 5.0.0-dev (2019-01-22) - stage: pretest name: Check if Linux tests are up-to-date diff --git a/Package@swift-5.0.swift b/Package@swift-5.0.swift new file mode 100644 index 0000000..126ddc1 --- /dev/null +++ b/Package@swift-5.0.swift @@ -0,0 +1,20 @@ +// swift-tools-version:5.0 +import PackageDescription + +let pkg = Package( + name: "Path.swift", + products: [ + .library(name: "Path", targets: ["Path"]), + ], + targets: [ + .target(name: "Path", path: "Sources"), + .testTarget(name: "PathTests", dependencies: ["Path"]), + ] +) + +pkg.platforms = [ + .macOS(.v10_10), .iOS(.v8), .tvOS(.v10), .watchOS(.v3) +] +pkg.swiftLanguageVersions = [ + .v4_2, .v5 +] diff --git a/Sources/Extensions.swift b/Sources/Extensions.swift index b3184ea..be9ec0a 100644 --- a/Sources/Extensions.swift +++ b/Sources/Extensions.swift @@ -10,17 +10,17 @@ public extension Bundle { } /// Returns the path for the shared-frameworks directory in this bundle. - public var sharedFrameworks: Path? { + var sharedFrameworks: Path? { return sharedFrameworksPath.flatMap(Path.init) } /// Returns the path for the resources directory in this bundle. - public var resources: Path? { + var resources: Path? { return resourcePath.flatMap(Path.init) } /// Returns the path for this bundle. - public var path: Path { + var path: Path { return Path(string: bundlePath) } } diff --git a/Sources/Path+Attributes.swift b/Sources/Path+Attributes.swift index 613c473..c30b940 100644 --- a/Sources/Path+Attributes.swift +++ b/Sources/Path+Attributes.swift @@ -4,7 +4,7 @@ public extension Path { /// - Note: If file is already locked, does nothing /// - Note: If file doesn’t exist, throws @discardableResult - public func lock() throws -> Path { + func lock() throws -> Path { var attrs = try FileManager.default.attributesOfItem(atPath: string) let b = attrs[.immutable] as? Bool ?? false if !b { @@ -17,7 +17,7 @@ public extension Path { /// - Note: If file isn‘t locked, does nothing /// - Note: If file doesn’t exist, does nothing @discardableResult - public func unlock() throws -> Path { + func unlock() throws -> Path { var attrs: [FileAttributeKey: Any] do { attrs = try FileManager.default.attributesOfItem(atPath: string) @@ -38,7 +38,7 @@ public extension Path { Path.home.join("foo").chmod(0o555) */ @discardableResult - public func chmod(_ octal: Int) throws -> Path { + func chmod(_ octal: Int) throws -> Path { try FileManager.default.setAttributes([.posixPermissions: octal], ofItemAtPath: string) return self } @@ -48,10 +48,10 @@ public extension Path { - Note: Returns the creation time if there is no modification time. - Note: Returns UNIX-time-zero if neither are available, though this *should* be impossible. */ - public var mtime: Date { + var mtime: Date { do { let attrs = try FileManager.default.attributesOfItem(atPath: string) - return attrs[.modificationDate] as? Date ?? attrs[.creationDate] as? Date ?? Date() + return attrs[.modificationDate] as? Date ?? attrs[.creationDate] as? Date ?? Date(timeIntervalSince1970: 0) } catch { //TODO log error return Date(timeIntervalSince1970: 0) diff --git a/Sources/Path+FileManager.swift b/Sources/Path+FileManager.swift index 2202229..26ce038 100644 --- a/Sources/Path+FileManager.swift +++ b/Sources/Path+FileManager.swift @@ -5,13 +5,14 @@ public extension Path { Copies a file. - Note: `throws` if `to` is a directory. - Parameter to: Destination filename. - - Parameter overwrite: If true overwrites any file that already exists at `to`. + - Parameter overwrite: If `true` and both `self` and `to` are files, overwrites `to`. + - Note: If either `self` or `to are directories, `overwrite` is ignored. - Returns: `to` to allow chaining - SeeAlso: `copy(into:overwrite:)` */ @discardableResult - public func copy(to: Path, overwrite: Bool = false) throws -> Path { - if overwrite, to.exists { + func copy(to: Path, overwrite: Bool = false) throws -> Path { + if overwrite, to.isFile, isFile { try FileManager.default.removeItem(at: to.url) } try FileManager.default.copyItem(atPath: string, toPath: to.string) @@ -33,7 +34,7 @@ public extension Path { - SeeAlso: `copy(into:overwrite:)` */ @discardableResult - public func copy(into: Path, overwrite: Bool = false) throws -> Path { + func copy(into: Path, overwrite: Bool = false) throws -> Path { if !into.exists { try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true) } @@ -42,7 +43,7 @@ public extension Path { try rv.delete() } #if os(Linux) - #if swift(>=5) + #if swift(>=5.1) // check if fixed #else if !overwrite, rv.isFile { @@ -63,7 +64,7 @@ public extension Path { - SeeAlso: move(into:overwrite:) */ @discardableResult - public func move(to: Path, overwrite: Bool = false) throws -> Path { + func move(to: Path, overwrite: Bool = false) throws -> Path { if overwrite, to.exists { try FileManager.default.removeItem(at: to.url) } @@ -83,7 +84,7 @@ public extension Path { - SeeAlso: move(into:overwrite:) */ @discardableResult - public func move(into: Path) throws -> Path { + func move(into: Path) throws -> Path { if !into.exists { try into.mkpath() } else if !into.isDirectory { @@ -96,7 +97,7 @@ public extension Path { /// Deletes the path, recursively if a directory. @inlinable - public func delete() throws { + func delete() throws { try FileManager.default.removeItem(at: url) } @@ -136,7 +137,7 @@ public extension Path { - Returns: `self` to allow chaining. */ @discardableResult - public func mkdir() throws -> Path { + func mkdir() throws -> Path { try _foo { try FileManager.default.createDirectory(at: self.url, withIntermediateDirectories: false, attributes: nil) } @@ -149,39 +150,10 @@ public extension Path { - Returns: `self` to allow chaining. */ @discardableResult - public func mkpath() throws -> Path { + func mkpath() throws -> Path { try _foo { try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) } return self } - - /** - Replaces the contents of the file at this path with the provided string. - - Note: If file doesn’t exist, creates file - - Note: If file is not writable, makes writable first, resetting permissions after the write - - Parameter contents: The string that will become the contents of this file. - - Parameter atomically: If `true` the operation will be performed atomically. - - Parameter encoding: The string encoding to use. - - Returns: `self` to allow chaining. - */ - @discardableResult - public func replaceContents(with contents: String, atomically: Bool = false, encoding: String.Encoding = .utf8) throws -> Path { - let resetPerms: Int? - if exists, !isWritable { - resetPerms = try FileManager.default.attributesOfItem(atPath: string)[.posixPermissions] as? Int - let perms = resetPerms ?? 0o777 - try chmod(perms | 0o200) - } else { - resetPerms = nil - } - - defer { - _ = try? resetPerms.map(self.chmod) - } - - try contents.write(to: self) - - return self - } }