Skip to content

Commit 54b1820

Browse files
authored
Merge pull request #12 from enuance/CPFeature-010-AddSetupTestsForCLI
CPFeature-010 Add Setup For Tests On CLI
2 parents e65242a + d19e066 commit 54b1820

File tree

17 files changed

+588
-119
lines changed

17 files changed

+588
-119
lines changed

.swiftpm/xcode/xcshareddata/xcschemes/commitPrefix.xcscheme

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,48 @@
3434
ReferencedContainer = "container:">
3535
</BuildableReference>
3636
</BuildActionEntry>
37+
<BuildActionEntry
38+
buildForTesting = "YES"
39+
buildForRunning = "YES"
40+
buildForProfiling = "YES"
41+
buildForArchiving = "YES"
42+
buildForAnalyzing = "YES">
43+
<BuildableReference
44+
BuildableIdentifier = "primary"
45+
BlueprintIdentifier = "CLInterface"
46+
BuildableName = "CLInterface"
47+
BlueprintName = "CLInterface"
48+
ReferencedContainer = "container:">
49+
</BuildableReference>
50+
</BuildActionEntry>
51+
<BuildActionEntry
52+
buildForTesting = "YES"
53+
buildForRunning = "YES"
54+
buildForProfiling = "YES"
55+
buildForArchiving = "YES"
56+
buildForAnalyzing = "YES">
57+
<BuildableReference
58+
BuildableIdentifier = "primary"
59+
BlueprintIdentifier = "FoundationExtensions"
60+
BuildableName = "FoundationExtensions"
61+
BlueprintName = "FoundationExtensions"
62+
ReferencedContainer = "container:">
63+
</BuildableReference>
64+
</BuildActionEntry>
65+
<BuildActionEntry
66+
buildForTesting = "YES"
67+
buildForRunning = "YES"
68+
buildForProfiling = "YES"
69+
buildForArchiving = "YES"
70+
buildForAnalyzing = "YES">
71+
<BuildableReference
72+
BuildableIdentifier = "primary"
73+
BlueprintIdentifier = "FoundationExt"
74+
BuildableName = "FoundationExt"
75+
BlueprintName = "FoundationExt"
76+
ReferencedContainer = "container:">
77+
</BuildableReference>
78+
</BuildActionEntry>
3779
</BuildActionEntries>
3880
</BuildAction>
3981
<TestAction

Package.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
// swift-tools-version:5.1
2-
// The swift-tools-version declares the minimum version of Swift required to build this package.
32

43
import PackageDescription
54

65
let package = Package(
76
name: "commitPrefix",
7+
platforms: [.macOS(.v10_15)],
88
dependencies: [
99
// 📁 John Sundell's Files Package is great for easy file reading/writing/moving/etc.
1010
.package(url: "https://github.com/JohnSundell/Files", from: "4.0.0"),
1111
// 🧰 SPMUtilities for CLI Argument Parsing.
1212
.package(url: "https://github.com/apple/swift-package-manager", from: "0.5.0"),
13-
// Consler for Styled outputs to the Console
13+
// 🖥 Consler for Styled outputs to the Console
1414
.package(url: "https://github.com/enuance/consler", from: "0.4.0")
1515
],
1616
targets: [
17+
.target(
18+
name: "FoundationExt",
19+
dependencies: [],
20+
path: "Sources/FoundationExt"),
21+
.target(
22+
name: "CLInterface",
23+
dependencies: ["SPMUtility", "Consler"],
24+
path: "Sources/CLInterface"),
1725
.target(
1826
name: "commitPrefix",
19-
dependencies: ["Files", "SPMUtility", "Consler"],
20-
// Normally don't have to specify the path, but I wan't the actual executable to be
21-
// lowercase and SPM brings folders in Uppercased by default.
27+
dependencies: ["Files", "Consler", "CLInterface", "FoundationExt"],
2228
path: "Sources/CommitPrefix"),
2329
.testTarget(
2430
name: "CommitPrefixTests",
25-
dependencies: ["commitPrefix"]),
31+
dependencies: ["CLInterface","commitPrefix", "FoundationExt"]
32+
)
2633
]
2734
)

Sources/CommitPrefix/CLInterface/CLIArguments.swift renamed to Sources/CLInterface/CLIArguments.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
import Foundation
2828
import SPMUtility
2929

30-
struct CLIArguments {
30+
public struct CLIArguments {
3131

32-
enum UserCommand {
32+
public enum UserCommand {
3333
case outputVersion
3434
case viewState
3535
case outputPrefixes
@@ -59,7 +59,7 @@ struct CLIArguments {
5959
private let modeBranchParse: OptionArgument<Bool>
6060
private let userEntry: PositionalArgument<[String]>
6161

62-
init(arguments: [String] = CommandLine.arguments) {
62+
public init(arguments: [String] = CommandLine.arguments) {
6363
// The first argument specifies the path of the executable file
6464
self.rawArgs = Array(arguments.dropFirst())
6565
let argBuilder = ArgumentBuilder()
@@ -73,7 +73,7 @@ struct CLIArguments {
7373
self.userEntry = argBuilder.buildUserEntryArgument(parser: parser)
7474
}
7575

76-
private func singleCommandParse(_ allCommands: [ParsedCommand]) -> Result<UserCommand, CPError> {
76+
private func singleCommandParse(_ allCommands: [ParsedCommand]) -> Result<UserCommand, CLIError> {
7777
precondition(allCommands.count == 1, "Intended for single Parsed Command only!")
7878
guard let foundCommand = allCommands.first else {
7979
return .failure(.commandNotRecognized)
@@ -95,7 +95,7 @@ struct CLIArguments {
9595
}
9696
}
9797

98-
private func doubleCommandParse(_ allCommands: [ParsedCommand]) -> Result<UserCommand, CPError> {
98+
private func doubleCommandParse(_ allCommands: [ParsedCommand]) -> Result<UserCommand, CLIError> {
9999
precondition(allCommands.count == 2, "Intended for two Parsed Commands only!")
100100
let firstCommand = allCommands[0]
101101
let secondCommand = allCommands[1]
@@ -110,7 +110,7 @@ struct CLIArguments {
110110
}
111111
}
112112

113-
private func getCommand() -> Result<UserCommand, CPError> {
113+
public func getCommand() -> Result<UserCommand, CLIError> {
114114
guard let parsedArgs = try? parser.parse(rawArgs) else {
115115
return .failure(.commandNotRecognized)
116116
}
@@ -126,12 +126,12 @@ struct CLIArguments {
126126
do {
127127
try parsedArgs.get(userEntry).map { userEntry in
128128
let noMoreThanOneEntry = userEntry.count < 2
129-
guard noMoreThanOneEntry else { throw CPError.invalidEntryFormat }
130-
guard let theEntry = userEntry.first else { throw CPError.emptyEntry }
129+
guard noMoreThanOneEntry else { throw CLIError.invalidEntryFormat }
130+
guard let theEntry = userEntry.first else { throw CLIError.emptyEntry }
131131
allCommands.append(.userEntry(value: theEntry))
132132
}
133-
} catch let cpError as CPError {
134-
return .failure(cpError)
133+
} catch let cliError as CLIError {
134+
return .failure(cliError)
135135
} catch {
136136
return .failure(.unexpectedError)
137137
}
@@ -149,6 +149,6 @@ struct CLIArguments {
149149

150150
}
151151

152-
var command: UserCommand { getCommand().resolveOrExit() }
152+
public var command: UserCommand { getCommand().resolveOrExit() }
153153

154154
}

Sources/CLInterface/CLIError.swift

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//
2+
// CLIError.swift
3+
// commitPrefix
4+
//
5+
// MIT License
6+
//
7+
// Copyright (c) 2020 STEPHEN L. MARTINEZ
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 all
17+
// 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 THE
25+
// SOFTWARE.
26+
27+
import Foundation
28+
import Consler
29+
30+
public enum TerminationStatus: Int32 {
31+
/// Used when the app finishes as expected
32+
case successful
33+
34+
/// Used when an error that has not been accounted for has been thrown
35+
case unexpectedError
36+
37+
/// Used when the user takes an action that stops the application short
38+
case userInitiated
39+
40+
/// Used when the inputs provided to the app are invalid
41+
case invalidInputs
42+
43+
/// Used when the app can no longer continue due to user specified settings
44+
case invalidContext
45+
46+
/// Used when required resources are inaccessible or unavailable
47+
case unavailableDependencies
48+
49+
public var value: Int32 { self.rawValue }
50+
}
51+
52+
public enum CLIError: Error, Equatable {
53+
54+
case commandNotRecognized
55+
case tooManyArguments
56+
case emptyEntry
57+
case invalidEntryFormat
58+
case unexpectedError
59+
60+
public var message: ConslerOutput {
61+
switch self {
62+
case .commandNotRecognized:
63+
return ConslerOutput(
64+
"Error: ", "Command not recognized. Enter ", "\"--help\"", " for usage.")
65+
.describedBy(.boldRed, .normal, .cyan)
66+
67+
case .tooManyArguments:
68+
return ConslerOutput(
69+
"Error: ", "Too many arguments entered. Only two at a time is supported.")
70+
.describedBy(.boldRed)
71+
72+
case .emptyEntry:
73+
return ConslerOutput("Error: ", "Your entry is empty.").describedBy(.boldRed)
74+
75+
case .invalidEntryFormat:
76+
return ConslerOutput("Error: ", "Your entry contains invalid spaces.")
77+
.describedBy(.boldRed)
78+
79+
case .unexpectedError:
80+
return ConslerOutput("Error: ", "An uncategorized error has occured")
81+
.describedBy(.boldRed)
82+
}
83+
}
84+
85+
public var status: TerminationStatus {
86+
switch self {
87+
case .commandNotRecognized:
88+
return .invalidInputs
89+
case .tooManyArguments:
90+
return .invalidInputs
91+
case .emptyEntry:
92+
return .invalidInputs
93+
case .invalidEntryFormat:
94+
return .invalidInputs
95+
case .unexpectedError:
96+
return .unexpectedError
97+
}
98+
}
99+
100+
}
101+
102+
extension Result where Failure == CLIError {
103+
104+
func resolveOrExit() -> Success {
105+
switch self {
106+
case let .success(value):
107+
return value
108+
case let .failure(cliError):
109+
Consler.output(cliError.message, type: .error)
110+
exit(cliError.status.value)
111+
}
112+
}
113+
114+
}

Sources/CommitPrefix/CPInteractor.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import Consler
2828
import Files
2929
import Foundation
30+
import FoundationExt
3031

3132
struct CPInteractor {
3233

Sources/CommitPrefix/Constants.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import Foundation
2828

2929
struct CPInfo {
3030

31-
static let version = "1.4.3"
31+
static let version = "1.4.4"
3232

3333
}
3434

0 commit comments

Comments
 (0)