Skip to content

Commit 3a423ec

Browse files
committed
feat: init
0 parents  commit 3a423ec

File tree

11 files changed

+370
-0
lines changed

11 files changed

+370
-0
lines changed

.github/workflows/swift.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Swift
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build:
11+
12+
runs-on: macos-latest
13+
14+
steps:
15+
- uses: actions/checkout@v2
16+
- name: Build
17+
run: swift build -v
18+
- name: Run tests
19+
run: swift test -v

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/
6+
DerivedData/
7+
.swiftpm

Commands.podspec

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Pod::Spec.new do |s|
2+
s.name = "Commands"
3+
s.version = "0.1.0"
4+
s.summary = "Swift utilities for running commands."
5+
s.homepage = "https://github.com/qiuzhifei/swift-commands"
6+
s.license = { :type => "MIT" }
7+
s.authors = { "lingoer" => "qiuzhifei521@gmail.com", "tangplin" => "qiuzhifei521@gmail.com" }
8+
9+
s.requires_arc = true
10+
s.swift_versions = ['5.1', '5.2', '5.3']
11+
s.osx.deployment_target = "10.9"
12+
s.source = { :git => "https://github.com/qiuzhifei/swift-commands.git", :tag => s.version }
13+
s.source_files = "Sources/*/*.swift"
14+
end

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2021 qiuzhifei <qiuzhifei521@gmail.com>
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

Package.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// swift-tools-version:5.3
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "swift-commands",
8+
products: [
9+
.library(name: "Commands", targets: ["Commands"]),
10+
],
11+
dependencies: [],
12+
targets: [
13+
.target(name: "Commands", dependencies: []),
14+
.testTarget(name: "CommandsTests", dependencies: ["Commands"])
15+
]
16+
)

README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Swift Commands
2+
![SPM](https://img.shields.io/badge/SPM-compatible-brightgreen.svg)
3+
![CocoaPods](https://img.shields.io/cocoapods/v/Commands.svg)
4+
[![CI Status](https://img.shields.io/github/workflow/status/qiuzhifei/swift-commands/Swift)](https://github.com/qiuzhifei/swift-commands/actions)
5+
[![License](https://img.shields.io/github/license/qiuzhifei/swift-commands)](https://github.com/qiuzhifei/swift-commands/blob/main/LICENSE)
6+
![Platform](https://img.shields.io/badge/platforms-macOS%2010.9-orange)
7+
8+
Swift utilities for running commands.
9+
10+
The `Commands` module allows you to take a system command as a string and return the standard output.
11+
12+
## Usage
13+
```
14+
import Commands
15+
```
16+
17+
Executes command in a subprocess.
18+
```
19+
Commands.Bash.system("ls")
20+
```
21+
22+
Returns the `Commands.Result` of running cmd in a subprocess.
23+
```
24+
let lsResult = Commands.Bash.run("ls")
25+
switch lsResult {
26+
case .Success(let output):
27+
debugPrint("success output: \(output)")
28+
case .Failure(let code, let output):
29+
debugPrint("failure code: \(code), output: \(output)")
30+
}
31+
```
32+
33+
## Adding Commands as a Dependency
34+
To use the `Commands` library in a SwiftPM project,
35+
add the following line to the dependencies in your `Package.swift` file:
36+
37+
```swift
38+
let package = Package(
39+
// name, platforms, products, etc.
40+
dependencies: [
41+
.package(url: "https://github.com/qiuzhifei/swift-commands", from: "0.1.0"),
42+
// other dependencies
43+
],
44+
targets: [
45+
.target(name: "<command-line-tool>", dependencies: [
46+
.product(name: "Commands", package: "swift-commands"),
47+
]),
48+
// other targets
49+
]
50+
)
51+
```
52+
53+
## CocoaPods (OS X 10.9+)
54+
You can use [CocoaPods](http://cocoapods.org/) to install `Commands` by adding it to your `Podfile`:
55+
```ruby
56+
pod 'Commands', '~> 0.1.0'
57+
```
58+
59+
## References
60+
- [https://github.com/apple/swift-argument-parser](https://github.com/apple/swift-argument-parser)

Sources/Commands/Commands.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// Commands.swift
3+
//
4+
//
5+
// Created by zhifei qiu on 2021/8/2.
6+
//
7+
8+
public enum Commands { }
9+
10+
public extension Commands {
11+
static let Bash = BashENV()
12+
13+
static let Ruby = RubyENV()
14+
15+
static let Python = PythonENV()
16+
17+
static func Custom(_ executableURL: String, dashc: String) -> CustomENV {
18+
return CustomENV(executableURL, dashc: dashc)
19+
}
20+
}

Sources/Commands/CommandsENV.swift

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//
2+
// CommandsENV.swift
3+
//
4+
//
5+
// Created by zhifei qiu on 2021/8/2.
6+
//
7+
8+
import Foundation
9+
10+
public protocol CommandsENV {
11+
var executableURL: String { get }
12+
var dashc: String { get }
13+
14+
func env()
15+
16+
func run(_ command: String) -> Commands.Result
17+
func system(_ command: String)
18+
}
19+
20+
public extension CommandsENV {
21+
func env() {
22+
let desc = """
23+
ExecutableURL: \(executableURL)
24+
Dashc: \(dashc)
25+
"""
26+
print(desc)
27+
}
28+
29+
@discardableResult
30+
func run(_ command: String) -> Commands.Result {
31+
let process = prepare(command)
32+
33+
let output = Pipe()
34+
process.standardOutput = output
35+
36+
let error = Pipe()
37+
process.standardError = error
38+
39+
run(process)
40+
41+
let outputData = output.fileHandleForReading.readDataToEndOfFile()
42+
let outputActual = String(data: outputData, encoding: .utf8)!.trimmingCharacters(in: .whitespacesAndNewlines)
43+
44+
let errorData = error.fileHandleForReading.readDataToEndOfFile()
45+
let errorActual = String(data: errorData, encoding: .utf8)!.trimmingCharacters(in: .whitespacesAndNewlines)
46+
47+
if process.terminationStatus == EXIT_SUCCESS {
48+
return Commands.Result.Success(output: outputActual)
49+
}
50+
51+
return Commands.Result.Failure(code: process.terminationStatus, output: errorActual)
52+
}
53+
54+
func system(_ command: String) {
55+
let process = prepare(command)
56+
run(process)
57+
}
58+
}
59+
60+
private extension CommandsENV {
61+
func prepare(_ command: String) -> Process {
62+
let process = Process()
63+
if #available(macOS 10.13, *) {
64+
process.executableURL = URL(fileURLWithPath: executableURL)
65+
} else {
66+
process.launchPath = executableURL
67+
}
68+
process.arguments = [dashc, command]
69+
return process
70+
}
71+
72+
func run(_ process: Process) {
73+
if #available(macOS 10.13, *) {
74+
try? process.run()
75+
} else {
76+
process.launch()
77+
}
78+
process.waitUntilExit()
79+
}
80+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// CommandsENVWrapper.swift
3+
//
4+
//
5+
// Created by zhifei qiu on 2021/8/3.
6+
//
7+
8+
import Foundation
9+
10+
// MARK: - Custom
11+
12+
public extension Commands {
13+
struct CustomENV: CommandsENV {
14+
public var executableURL: String
15+
public var dashc: String
16+
17+
init(_ executableURL: String, dashc: String) {
18+
self.executableURL = executableURL
19+
self.dashc = dashc
20+
}
21+
}
22+
}
23+
24+
// MARK: - Bash
25+
26+
public extension Commands {
27+
struct BashENV: CommandsENV {
28+
public var executableURL: String = "/bin/bash"
29+
public var dashc: String = "-c"
30+
}
31+
}
32+
33+
// MARK: - Ruby
34+
35+
public extension Commands {
36+
struct RubyENV: CommandsENV {
37+
public var executableURL: String = {
38+
return Bash.run("which ruby").output
39+
}()
40+
public var dashc: String = "-e"
41+
}
42+
}
43+
44+
// MARK: - Python
45+
46+
public extension Commands {
47+
struct PythonENV: CommandsENV {
48+
public var executableURL: String = {
49+
return Bash.run("which python").output
50+
}()
51+
public var dashc: String = "-c"
52+
}
53+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// CommandsResult.swift
3+
//
4+
//
5+
// Created by zhifei qiu on 2021/8/2.
6+
//
7+
8+
public extension Commands {
9+
enum Result {
10+
case Success(output: String)
11+
case Failure(code: Int32, output: String)
12+
}
13+
}
14+
15+
public extension Commands.Result {
16+
var output: String {
17+
switch self {
18+
case .Success(let output):
19+
return output
20+
case .Failure(_, let output):
21+
return output
22+
}
23+
}
24+
}
25+
26+
public extension Commands.Result {
27+
var isSuccess: Bool {
28+
switch self {
29+
case .Success(_):
30+
return true
31+
default:
32+
return false
33+
}
34+
}
35+
36+
var isFailure: Bool {
37+
!isSuccess
38+
}
39+
}

0 commit comments

Comments
 (0)