diff --git a/ReactiveTask.xcodeproj/project.pbxproj b/ReactiveTask.xcodeproj/project.pbxproj index e2aae50..010d3ce 100644 --- a/ReactiveTask.xcodeproj/project.pbxproj +++ b/ReactiveTask.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ CD52F6441DD0411100B2F764 /* Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD52F6431DD0411100B2F764 /* Availability.swift */; }; + CD72E5711E7A4A4800E2DE29 /* StringExtensionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD72E5701E7A4A4800E2DE29 /* StringExtensionSpec.swift */; }; D02130921AF87B6500B9EC20 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D02130911AF87B6500B9EC20 /* Result.framework */; }; D0BFEA5E1A2D1E5E00E23194 /* ReactiveTask.h in Headers */ = {isa = PBXBuildFile; fileRef = D0BFEA5D1A2D1E5E00E23194 /* ReactiveTask.h */; settings = {ATTRIBUTES = (Public, ); }; }; D0BFEA641A2D1E5E00E23194 /* ReactiveTask.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BFEA581A2D1E5E00E23194 /* ReactiveTask.framework */; }; @@ -31,6 +32,7 @@ /* Begin PBXFileReference section */ CD52F6431DD0411100B2F764 /* Availability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Availability.swift; sourceTree = "<group>"; }; + CD72E5701E7A4A4800E2DE29 /* StringExtensionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensionSpec.swift; sourceTree = "<group>"; }; D02130911AF87B6500B9EC20 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0BFEA581A2D1E5E00E23194 /* ReactiveTask.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveTask.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0BFEA5C1A2D1E5E00E23194 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; @@ -132,6 +134,7 @@ D0BFEA671A2D1E5E00E23194 /* ReactiveTaskTests */ = { isa = PBXGroup; children = ( + CD72E5701E7A4A4800E2DE29 /* StringExtensionSpec.swift */, D0BFEAA11A2D212800E23194 /* TaskSpec.swift */, D0BFEA681A2D1E5E00E23194 /* Supporting Files */, ); @@ -336,6 +339,7 @@ buildActionMask = 2147483647; files = ( D0BFEAA21A2D212800E23194 /* TaskSpec.swift in Sources */, + CD72E5711E7A4A4800E2DE29 /* StringExtensionSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/Task.swift b/Sources/Task.swift index 7bd81fb..ba53d8d 100644 --- a/Sources/Task.swift +++ b/Sources/Task.swift @@ -46,19 +46,21 @@ public struct Task { } } -private extension String { - var escaped: String { - if rangeOfCharacter(from: .whitespaces) != nil { - return "\"\(self)\"" - } else { - return self - } +extension String { + private static let whitespaceRegularExpression = try! NSRegularExpression(pattern: "\\s") + + var escapingWhitespaces: String { + return String.whitespaceRegularExpression.stringByReplacingMatches( + in: self, + range: NSRange(location: 0, length: self.utf16.count), + withTemplate: "\\\\$0" + ) } } extension Task: CustomStringConvertible { public var description: String { - return "\(launchPath) \(arguments.map { $0.escaped }.joined(separator: " "))" + return "\(launchPath) \(arguments.map { $0.escapingWhitespaces }.joined(separator: " "))" } } diff --git a/Tests/ReactiveTaskTests/StringExtensionSpec.swift b/Tests/ReactiveTaskTests/StringExtensionSpec.swift new file mode 100644 index 0000000..136700b --- /dev/null +++ b/Tests/ReactiveTaskTests/StringExtensionSpec.swift @@ -0,0 +1,18 @@ +import Quick +import Nimble +@testable import ReactiveTask + +class StringExtensionSpec: QuickSpec { + override func spec() { + describe("escapingWhitespaces") { + it("should escape whitespace characters") { + expect("a b c".escapingWhitespaces).to(equal("a\\ b\\ c")) + expect("d\te\tf".escapingWhitespaces).to(equal("d\\\te\\\tf")) + } + + it("should not change the original string if it does not contain whitespaces") { + expect("ReactiveTask").to(equal("ReactiveTask")) + } + } + } +}