Skip to content

Commit f5dedb9

Browse files
Merge pull request #1 from finestructure/safe-api
Safe api
2 parents b62dc4d + 387fa4c commit f5dedb9

File tree

7 files changed

+319
-164
lines changed

7 files changed

+319
-164
lines changed

Package.resolved

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,15 @@ let package = Package(
1313
products: [
1414
.library(name: "ShellOut", targets: ["ShellOut"])
1515
],
16+
dependencies: [
17+
.package(url: "https://github.com/SwiftPackageIndex/ShellQuote", from: "1.0.2"),
18+
],
1619
targets: [
1720
.target(
1821
name: "ShellOut",
22+
dependencies: [
23+
.product(name: "ShellQuote", package: "ShellQuote")
24+
],
1925
path: "Sources"
2026
),
2127
.testTarget(

Sources/Argument.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import Foundation
2+
3+
4+
public enum Argument {
5+
case quoted(QuotedString)
6+
case verbatim(String)
7+
8+
public init(quoted string: String) {
9+
self = .quoted(.init(string))
10+
}
11+
12+
public init(verbatim string: String) {
13+
self = .verbatim(string)
14+
}
15+
16+
public var string: String {
17+
switch self {
18+
case let .quoted(value):
19+
return value.quoted
20+
case let .verbatim(string):
21+
return string
22+
}
23+
}
24+
}
25+
26+
27+
extension Argument: ExpressibleByStringLiteral {
28+
public init(stringLiteral value: StringLiteralType) {
29+
self = .quoted(.init(value))
30+
}
31+
}
32+
33+
34+
extension Argument: CustomStringConvertible {
35+
public var description: String { string }
36+
}
37+
38+
39+
extension Argument {
40+
public static func url(_ url: URL) -> Self { url.absoluteString.verbatim }
41+
}
42+
43+
44+
extension String {
45+
public var quoted: Argument { .init(quoted: self) }
46+
public var verbatim: Argument { .init(verbatim: self) }
47+
}
48+
49+
50+
extension Array where Element == String {
51+
public var quoted: [Argument] { map(\.quoted) }
52+
public var verbatim: [Argument] { map(\.verbatim) }
53+
}

Sources/QuotedString.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import ShellQuote
2+
3+
4+
public struct QuotedString {
5+
public var unquoted: String
6+
public var quoted: String
7+
8+
public init(_ value: String) {
9+
self.unquoted = value
10+
self.quoted = ShellQuote.quote(value)
11+
}
12+
}
13+
14+
15+
extension QuotedString: CustomStringConvertible {
16+
public var description: String { quoted }
17+
}

Sources/SafeString.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import ShellQuote
2+
3+
4+
public struct SafeString {
5+
public var value: String
6+
7+
public init(_ value: String) throws {
8+
guard !ShellQuote.hasUnsafeContent(value) else {
9+
throw ShellOutCommand.Error(message: "Command must not contain characters that require quoting, was: \(value)")
10+
}
11+
self.value = value
12+
}
13+
14+
public init(unchecked value: String) {
15+
self.value = value
16+
}
17+
}
18+
19+
extension SafeString: CustomStringConvertible {
20+
public var description: String { value }
21+
}
22+
23+
24+
extension String {
25+
public var checked: SafeString {
26+
get throws { try .init(self) }
27+
}
28+
public var unchecked: SafeString { .init(unchecked: self) }
29+
}

0 commit comments

Comments
 (0)