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
30 changes: 23 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
language: objective-c
script: placeholder # workaround for https://github.com/travis-ci/travis-ci/issues/4681
matrix:
exclude:
- script: placeholder # workaround for https://github.com/travis-ci/travis-ci/issues/4681
include:
- script: script/cibuild
osx_image: xcode7.2
env: JOB=Xcode7.2
xcode_workspace: Commandant.xcworkspace
xcode_scheme: Commandant
- script: script/cibuild
os: osx
osx_image: xcode7.3
language: objective-c
env: JOB=Xcode7.3
xcode_workspace: Commandant.xcworkspace
xcode_scheme: Commandant
- script: swift build
os: osx
osx_image: xcode7.3
language: objective-c
env: JOB=SPM
before_install:
- export SWIFT_VERSION=swift-DEVELOPMENT-SNAPSHOT-2016-03-01-a
- export SWIFT_VERSION=swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a
- curl https://swift.org/builds/development/xcode/$SWIFT_VERSION/$SWIFT_VERSION-osx.pkg -o swift.pkg
- sudo installer -pkg swift.pkg -target /
- export PATH="/Library/Developer/Toolchains/$SWIFT_VERSION.xctoolchain/usr/bin:$PATH"
- script: swift build
env: JOB=Linux
sudo: required
dist: trusty
language: generic
before_install:
- wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import -
- cd ..
- gpg --keyserver hkp://pool.sks-keyservers.net --refresh-keys Swift
- export SWIFT_SNAPSHOT=swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a
- export SWIFT_URL=https://swift.org/builds/development/ubuntu1404/$SWIFT_SNAPSHOT/$SWIFT_SNAPSHOT-ubuntu14.04.tar.gz
- wget $SWIFT_URL
- wget $SWIFT_URL.sig
- gpg --verify $SWIFT_SNAPSHOT-ubuntu14.04.tar.gz.sig
- tar xzf $SWIFT_SNAPSHOT-ubuntu14.04.tar.gz
- export PATH="${PWD}/${SWIFT_SNAPSHOT}-ubuntu14.04/usr/bin:${PATH}"
- cd Commandant

notifications:
email: false
slack:
Expand Down
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "antitypical/Result" ~> 1.0.2
github "antitypical/Result" ~> 2.0
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
github "Quick/Nimble" "v3.2.0"
github "Quick/Quick" "v0.9.1"
github "antitypical/Result" "1.0.2"
github "antitypical/Result" "2.0.0"
github "jspahrsummers/xcconfigs" "0.9"
4 changes: 4 additions & 0 deletions Commandant.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
6C29EE8B1CA6241B0005DA40 /* Swift3to22.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C29EE8A1CA6241B0005DA40 /* Swift3to22.swift */; };
CD2ED3411C1E6C5D0076092B /* Argument.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD2ED3401C1E6C5D0076092B /* Argument.swift */; };
CD2ED3431C1E6D540076092B /* ArgumentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD2ED3421C1E6D540076092B /* ArgumentType.swift */; };
CDFC88361C3C0612003AC8F8 /* CommandSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFC88351C3C0612003AC8F8 /* CommandSpec.swift */; };
Expand Down Expand Up @@ -35,6 +36,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
6C29EE8A1CA6241B0005DA40 /* Swift3to22.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Swift3to22.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
CD2ED3401C1E6C5D0076092B /* Argument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Argument.swift; sourceTree = "<group>"; };
CD2ED3421C1E6D540076092B /* ArgumentType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArgumentType.swift; sourceTree = "<group>"; };
CDFC88351C3C0612003AC8F8 /* CommandSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandSpec.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -231,6 +233,7 @@
D00CCE261A20741300109F8C /* Command.swift */,
D00CCE2A1A20748500109F8C /* Errors.swift */,
D00CCE2E1A2075F700109F8C /* Option.swift */,
6C29EE8A1CA6241B0005DA40 /* Swift3to22.swift */,
D8169D861ACB942D00923FB0 /* Switch.swift */,
);
name = Core;
Expand Down Expand Up @@ -355,6 +358,7 @@
files = (
CD2ED3411C1E6C5D0076092B /* Argument.swift in Sources */,
D0BF14FB1A4C8957003147BC /* HelpCommand.swift in Sources */,
6C29EE8B1CA6241B0005DA40 /* Swift3to22.swift in Sources */,
CD2ED3431C1E6D540076092B /* ArgumentType.swift in Sources */,
D00CCE2F1A2075F700109F8C /* Option.swift in Sources */,
D00CCE2B1A20748500109F8C /* Errors.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import PackageDescription
let package = Package(
name: "Commandant",
dependencies: [
.Package(url: "https://github.com/antitypical/Result.git", majorVersion: 1)
.Package(url: "https://github.com/antitypical/Result.git", majorVersion: 2)
]
)
14 changes: 7 additions & 7 deletions Sources/Commandant/ArgumentParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ public final class ArgumentParser {
/// Initializes the generator from a simple list of command-line arguments.
public init(_ arguments: [String]) {
// The first instance of `--` terminates the option list.
let params = arguments.split(1, allowEmptySlices: true) { $0 == "--" }
let params = arguments.split(maxSplits: 1, omittingEmptySubsequences: false) { $0 == "--" }

// Parse out the keyed and flag options.
let options = params.first!
rawArguments.appendContentsOf(options.map { arg in
rawArguments.append(contentsOf: options.map { arg in
if arg.hasPrefix("-") {
// Do we have `--{key}` or `-{flags}`.
let opt = arg.characters.dropFirst()
Expand All @@ -82,7 +82,7 @@ public final class ArgumentParser {
// Remaining arguments are all positional parameters.
if params.count == 2 {
let positional = params.last!
rawArguments.appendContentsOf(positional.map(RawArgument.Value))
rawArguments.append(contentsOf: positional.map(RawArgument.Value))
}
}

Expand Down Expand Up @@ -150,9 +150,9 @@ public final class ArgumentParser {
/// Returns the next positional argument that hasn't yet been returned, or
/// nil if there are no more positional arguments.
internal func consumePositionalArgument() -> String? {
for (index, arg) in rawArguments.enumerate() {
for (index, arg) in rawArguments.enumerated() {
if case let .Value(value) = arg {
rawArguments.removeAtIndex(index)
rawArguments.remove(at: index)
return value
}
}
Expand All @@ -172,13 +172,13 @@ public final class ArgumentParser {
/// Returns whether the given flag was specified and removes it from the
/// list of arguments remaining.
internal func consumeBooleanFlag(flag: Character) -> Bool {
for (index, arg) in rawArguments.enumerate() {
for (index, arg) in rawArguments.enumerated() {
if case let .Flag(flags) = arg where flags.contains(flag) {
var flags = flags
flags.remove(flag)

if flags.isEmpty {
rawArguments.removeAtIndex(index)
rawArguments.remove(at: index)
} else {
rawArguments[index] = .Flag(flags)
}
Expand Down
16 changes: 8 additions & 8 deletions Sources/Commandant/Command.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import Result
public protocol CommandType {

/// The command's options type.
typealias Options: OptionsType
associatedtype Options: OptionsType

typealias ClientError: ErrorType = Options.ClientError
associatedtype ClientError: ClientErrorType = Options.ClientError

/// The action that users should specify to use this subcommand (e.g.,
/// `help`).
Expand All @@ -30,7 +30,7 @@ public protocol CommandType {
}

/// A type-erased command.
public struct CommandWrapper<ClientError: ErrorType> {
public struct CommandWrapper<ClientError: ClientErrorType> {
public let verb: String
public let function: String

Expand Down Expand Up @@ -76,12 +76,12 @@ public enum CommandMode {
}

/// Maintains the list of commands available to run.
public final class CommandRegistry<ClientError: ErrorType> {
public final class CommandRegistry<ClientError: ClientErrorType> {
private var commandsByVerb: [String: CommandWrapper<ClientError>] = [:]

/// All available commands.
public var commands: [CommandWrapper<ClientError>] {
return commandsByVerb.values.sort { return $0.verb < $1.verb }
return commandsByVerb.values.sorted { return $0.verb < $1.verb }
}

public init() {}
Expand Down Expand Up @@ -154,12 +154,12 @@ extension CommandRegistry {
var arguments = arguments

// Extract the executable name.
let executableName = arguments.removeAtIndex(0)
let executableName = arguments.remove(at: 0)

let verb = arguments.first ?? defaultVerb
if arguments.count > 0 {
// Remove the command name.
arguments.removeAtIndex(0)
arguments.remove(at: 0)
}

switch runCommand(verb, arguments: arguments) {
Expand Down Expand Up @@ -192,7 +192,7 @@ extension CommandRegistry {
///
/// - Returns: The exit status of found subcommand or nil.
private func executeSubcommandIfExists(executableName: String, verb: String, arguments: [String]) -> Int32? {
let subcommand = "\((executableName as NSString).lastPathComponent)-\(verb)"
let subcommand = "\(NSString(string: executableName).lastPathComponent)-\(verb)"

func launchTask(path: String, arguments: [String]) -> Int32 {
let task = NSTask()
Expand Down
12 changes: 9 additions & 3 deletions Sources/Commandant/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@
import Foundation
import Result

#if swift(>=3)
public typealias ClientErrorType = ErrorProtocol
#else
public typealias ClientErrorType = ErrorType
#endif

/// Possible errors that can originate from Commandant.
///
/// `ClientError` should be the type of error (if any) that can occur when
/// running commands.
public enum CommandantError<ClientError>: ErrorType {
public enum CommandantError<ClientError>: ClientErrorType {
/// An option was used incorrectly.
case UsageError(description: String)

Expand Down Expand Up @@ -43,7 +49,7 @@ internal func missingArgumentError<ClientError>(argumentName: String) -> Command
/// Constructs an error by combining the example of key (and value, if applicable)
/// with the usage description.
internal func informativeUsageError<ClientError>(keyValueExample: String, usage: String) -> CommandantError<ClientError> {
let lines = usage.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
let lines = usage.componentsSeparatedByCharacters(in: NSCharacterSet.newline())

return .UsageError(description: lines.reduce(keyValueExample) { previous, value in
return previous + "\n\t" + value
Expand All @@ -68,7 +74,7 @@ internal func combineUsageErrors<ClientError>(lhs: CommandantError<ClientError>,

/// Constructs an error that indicates unrecognized arguments remains.
internal func unrecognizedArgumentsError<ClientError>(options: [String]) -> CommandantError<ClientError> {
return .UsageError(description: "Unrecognized arguments: " + options.joinWithSeparator(", "))
return .UsageError(description: "Unrecognized arguments: " + options.joined(separator: ", "))
}

// MARK: Argument
Expand Down
8 changes: 4 additions & 4 deletions Sources/Commandant/HelpCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import Result
/// let commands: CommandRegistry<MyErrorType> = …
/// let helpCommand = HelpCommand(registry: commands)
/// commands.register(helpCommand)
public struct HelpCommand<ClientError: ErrorType>: CommandType {
public struct HelpCommand<ClientError: ClientErrorType>: CommandType {
public typealias Options = HelpOptions<ClientError>

public let verb = "help"
Expand Down Expand Up @@ -47,18 +47,18 @@ public struct HelpCommand<ClientError: ErrorType>: CommandType {

print("Available commands:\n")

let maxVerbLength = self.registry.commands.map { $0.verb.characters.count }.maxElement() ?? 0
let maxVerbLength = self.registry.commands.map { $0.verb.characters.count }.max() ?? 0

for command in self.registry.commands {
let padding = Repeat<Character>(count: maxVerbLength - command.verb.characters.count, repeatedValue: " ")
let padding = repeatElement(Character(" "), count: maxVerbLength - command.verb.characters.count)
print(" \(command.verb)\(String(padding)) \(command.function)")
}

return .Success(())
}
}

public struct HelpOptions<ClientError: ErrorType>: OptionsType {
public struct HelpOptions<ClientError: ClientErrorType>: OptionsType {
private let verb: String?

private init(verb: String?) {
Expand Down
4 changes: 2 additions & 2 deletions Sources/Commandant/Option.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import Result
/// }
/// }
public protocol OptionsType {
typealias ClientError: ErrorType
associatedtype ClientError: ClientErrorType

/// Evaluates this set of options in the given mode.
///
Expand All @@ -44,7 +44,7 @@ public protocol OptionsType {
}

/// An `OptionsType` that has no options.
public struct NoOptions<ClientError: ErrorType>: OptionsType {
public struct NoOptions<ClientError: ClientErrorType>: OptionsType {
public init() {}

public static func evaluate(m: CommandMode) -> Result<NoOptions, CommandantError<ClientError>> {
Expand Down
70 changes: 70 additions & 0 deletions Sources/Commandant/Swift3to22.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// Swift3to22.swift
// Commandant
//
// Created by Norio Nomura on 3/26/16.
// Copyright © 2016 Carthage. All rights reserved.
//

import Foundation

#if !swift(>=3)
internal func repeatElement<T>(element: T, count: Int) -> Repeat<T> {
return Repeat(count: count, repeatedValue: element)
}

extension Array {
internal mutating func append<C : CollectionType where C.Generator.Element == Element>(contentsOf newElements: C) {
appendContentsOf(newElements)
}

internal mutating func remove(at index: Int) -> Element {
return removeAtIndex(index)
}
}

extension CollectionType {
internal func split(maxSplits maxSplits: Int = Int.max, omittingEmptySubsequences: Bool = true, @noescape isSeparator: (Generator.Element) throws -> Bool) rethrows -> [SubSequence] {
return try split(maxSplits, allowEmptySlices: !omittingEmptySubsequences, isSeparator: isSeparator)
}

}

extension SequenceType {
internal func enumerated() -> EnumerateSequence<Self> {
return enumerate()
}

internal func sorted(@noescape isOrderedBefore isOrderedBefore: (Generator.Element, Generator.Element) -> Bool) -> [Generator.Element] {
return sort(isOrderedBefore)
}
}

extension SequenceType where Generator.Element : Comparable {
internal func max() -> Generator.Element? {
return maxElement()
}
}

extension SequenceType where Generator.Element == String {
internal func joined(separator separator: String) -> String {
return joinWithSeparator(separator)
}
}

#endif

// swift-corelibs-foundation is still written in Swift 2 API.
#if !swift(>=3) || os(Linux)
extension NSCharacterSet {
class func newline() -> NSCharacterSet {
return newlineCharacterSet()
}
}

extension String {
func componentsSeparatedByCharacters(in separator: NSCharacterSet) -> [String] {
return componentsSeparatedByCharactersInSet(separator)
}
}
#endif