Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/commitPrefix.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,48 @@
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CLInterface"
BuildableName = "CLInterface"
BlueprintName = "CLInterface"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FoundationExtensions"
BuildableName = "FoundationExtensions"
BlueprintName = "FoundationExtensions"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FoundationExt"
BuildableName = "FoundationExt"
BlueprintName = "FoundationExt"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
Expand Down
19 changes: 13 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "commitPrefix",
platforms: [.macOS(.v10_15)],
dependencies: [
// 📁 John Sundell's Files Package is great for easy file reading/writing/moving/etc.
.package(url: "https://github.com/JohnSundell/Files", from: "4.0.0"),
// 🧰 SPMUtilities for CLI Argument Parsing.
.package(url: "https://github.com/apple/swift-package-manager", from: "0.5.0"),
// Consler for Styled outputs to the Console
// 🖥 Consler for Styled outputs to the Console
.package(url: "https://github.com/enuance/consler", from: "0.4.0")
],
targets: [
.target(
name: "FoundationExt",
dependencies: [],
path: "Sources/FoundationExt"),
.target(
name: "CLInterface",
dependencies: ["SPMUtility", "Consler"],
path: "Sources/CLInterface"),
.target(
name: "commitPrefix",
dependencies: ["Files", "SPMUtility", "Consler"],
// Normally don't have to specify the path, but I wan't the actual executable to be
// lowercase and SPM brings folders in Uppercased by default.
dependencies: ["Files", "Consler", "CLInterface", "FoundationExt"],
path: "Sources/CommitPrefix"),
.testTarget(
name: "CommitPrefixTests",
dependencies: ["commitPrefix"]),
dependencies: ["CLInterface","commitPrefix", "FoundationExt"]
)
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
import Foundation
import SPMUtility

struct CLIArguments {
public struct CLIArguments {

enum UserCommand {
public enum UserCommand {
case outputVersion
case viewState
case outputPrefixes
Expand Down Expand Up @@ -59,7 +59,7 @@ struct CLIArguments {
private let modeBranchParse: OptionArgument<Bool>
private let userEntry: PositionalArgument<[String]>

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

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

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

private func getCommand() -> Result<UserCommand, CPError> {
public func getCommand() -> Result<UserCommand, CLIError> {
guard let parsedArgs = try? parser.parse(rawArgs) else {
return .failure(.commandNotRecognized)
}
Expand All @@ -126,12 +126,12 @@ struct CLIArguments {
do {
try parsedArgs.get(userEntry).map { userEntry in
let noMoreThanOneEntry = userEntry.count < 2
guard noMoreThanOneEntry else { throw CPError.invalidEntryFormat }
guard let theEntry = userEntry.first else { throw CPError.emptyEntry }
guard noMoreThanOneEntry else { throw CLIError.invalidEntryFormat }
guard let theEntry = userEntry.first else { throw CLIError.emptyEntry }
allCommands.append(.userEntry(value: theEntry))
}
} catch let cpError as CPError {
return .failure(cpError)
} catch let cliError as CLIError {
return .failure(cliError)
} catch {
return .failure(.unexpectedError)
}
Expand All @@ -149,6 +149,6 @@ struct CLIArguments {

}

var command: UserCommand { getCommand().resolveOrExit() }
public var command: UserCommand { getCommand().resolveOrExit() }

}
114 changes: 114 additions & 0 deletions Sources/CLInterface/CLIError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//
// CLIError.swift
// commitPrefix
//
// MIT License
//
// Copyright (c) 2020 STEPHEN L. MARTINEZ
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Foundation
import Consler

public enum TerminationStatus: Int32 {
/// Used when the app finishes as expected
case successful

/// Used when an error that has not been accounted for has been thrown
case unexpectedError

/// Used when the user takes an action that stops the application short
case userInitiated

/// Used when the inputs provided to the app are invalid
case invalidInputs

/// Used when the app can no longer continue due to user specified settings
case invalidContext

/// Used when required resources are inaccessible or unavailable
case unavailableDependencies

public var value: Int32 { self.rawValue }
}

public enum CLIError: Error, Equatable {

case commandNotRecognized
case tooManyArguments
case emptyEntry
case invalidEntryFormat
case unexpectedError

public var message: ConslerOutput {
switch self {
case .commandNotRecognized:
return ConslerOutput(
"Error: ", "Command not recognized. Enter ", "\"--help\"", " for usage.")
.describedBy(.boldRed, .normal, .cyan)

case .tooManyArguments:
return ConslerOutput(
"Error: ", "Too many arguments entered. Only two at a time is supported.")
.describedBy(.boldRed)

case .emptyEntry:
return ConslerOutput("Error: ", "Your entry is empty.").describedBy(.boldRed)

case .invalidEntryFormat:
return ConslerOutput("Error: ", "Your entry contains invalid spaces.")
.describedBy(.boldRed)

case .unexpectedError:
return ConslerOutput("Error: ", "An uncategorized error has occured")
.describedBy(.boldRed)
}
}

public var status: TerminationStatus {
switch self {
case .commandNotRecognized:
return .invalidInputs
case .tooManyArguments:
return .invalidInputs
case .emptyEntry:
return .invalidInputs
case .invalidEntryFormat:
return .invalidInputs
case .unexpectedError:
return .unexpectedError
}
}

}

extension Result where Failure == CLIError {

func resolveOrExit() -> Success {
switch self {
case let .success(value):
return value
case let .failure(cliError):
Consler.output(cliError.message, type: .error)
exit(cliError.status.value)
}
}

}
1 change: 1 addition & 0 deletions Sources/CommitPrefix/CPInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import Consler
import Files
import Foundation
import FoundationExt

struct CPInteractor {

Expand Down
2 changes: 1 addition & 1 deletion Sources/CommitPrefix/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import Foundation

struct CPInfo {

static let version = "1.4.3"
static let version = "1.4.4"

}

Expand Down
Loading