Add —availability filter to list-devices
This commit is contained in:
parent
9a982c41e4
commit
60ea7523a1
|
@ -2,11 +2,23 @@ import Foundation
|
|||
import Basic
|
||||
import SPMUtility
|
||||
|
||||
struct FilteringOptions {
|
||||
enum Availability: String, StringEnumArgument {
|
||||
case yes
|
||||
case no
|
||||
case all
|
||||
|
||||
public static var completion: ShellCompletion = .none
|
||||
}
|
||||
|
||||
var availability: Availability = .yes
|
||||
}
|
||||
|
||||
struct CommandLineOptions {
|
||||
enum SubCommand {
|
||||
case noCommand
|
||||
case version
|
||||
case listDevices
|
||||
case command(Command)
|
||||
}
|
||||
|
||||
struct Error: Swift.Error {
|
||||
|
@ -22,10 +34,13 @@ struct CommandLineOptions {
|
|||
argumentParser.printUsage(on: stream)
|
||||
}
|
||||
}
|
||||
struct NoCommandError: Swift.Error { }
|
||||
|
||||
private let parser: ArgumentParser
|
||||
private(set) var subCommand: SubCommand
|
||||
|
||||
private static let allCommands: [Command] = [ListDevicesCommand()]
|
||||
|
||||
static func parse(commandName: String, arguments: [String]) throws -> CommandLineOptions {
|
||||
let parser = ArgumentParser(
|
||||
commandName: commandName,
|
||||
|
@ -36,29 +51,53 @@ struct CommandLineOptions {
|
|||
|
||||
let binder = ArgumentBinder<CommandLineOptions>()
|
||||
|
||||
binder.bind(option: parser.add(
|
||||
binder.bind(
|
||||
option: parser.add(
|
||||
option: "--version",
|
||||
shortName: "-v",
|
||||
kind: Bool.self,
|
||||
usage: "Prints the version and exits"
|
||||
), to: { options, _ in
|
||||
options.subCommand = .version
|
||||
})
|
||||
|
||||
parser.add(
|
||||
subparser: "list-devices",
|
||||
overview: "List available Xcode Simulator devices"
|
||||
}
|
||||
)
|
||||
|
||||
var options = CommandLineOptions(parser: parser, subCommand: .noCommand)
|
||||
do {
|
||||
let result = try parser.parse(arguments)
|
||||
switch result.subparser(parser) {
|
||||
case "list-devices":
|
||||
options.subCommand = .listDevices
|
||||
default:
|
||||
try binder.fill(parseResult: result, into: &options)
|
||||
binder.bind(
|
||||
parser: parser,
|
||||
to: { options, subcommand in
|
||||
print("Parsed subcommand: \(subcommand)")
|
||||
}
|
||||
)
|
||||
|
||||
for command in allCommands {
|
||||
let subparser = parser.add(
|
||||
subparser: command.name,
|
||||
overview: command.overview
|
||||
)
|
||||
command.addOptions(to: subparser)
|
||||
}
|
||||
|
||||
var options = CommandLineOptions(parser: parser, subCommand: .noCommand)
|
||||
checkresult: do {
|
||||
let result = try parser.parse(arguments)
|
||||
try binder.fill(parseResult: result, into: &options)
|
||||
|
||||
// In some cases like --version, we already have a subcommand and are thus already done
|
||||
guard case SubCommand.noCommand = options.subCommand else {
|
||||
break checkresult
|
||||
}
|
||||
|
||||
guard
|
||||
let subcommandName = result.subparser(parser),
|
||||
var command = allCommands.first(where: { $0.name == subcommandName })
|
||||
else {
|
||||
throw Error(
|
||||
underlyingError: NoCommandError(),
|
||||
argumentParser: parser
|
||||
)
|
||||
}
|
||||
|
||||
try command.fillParseResult(result)
|
||||
options.subCommand = SubCommand.command(command)
|
||||
} catch {
|
||||
throw Error(underlyingError: error, argumentParser: parser)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import Foundation
|
||||
import SPMUtility
|
||||
|
||||
protocol Command {
|
||||
/// Subcommand as it will be written on command-line
|
||||
var name: String { get }
|
||||
|
||||
/// Overview for help
|
||||
var overview: String { get }
|
||||
|
||||
func addOptions(to parser: ArgumentParser)
|
||||
mutating func fillParseResult(_ parseResult: ArgumentParser.Result) throws
|
||||
func run() throws
|
||||
}
|
|
@ -1,14 +1,57 @@
|
|||
import Foundation
|
||||
import SPMUtility
|
||||
|
||||
struct ListDevicesCommand {
|
||||
struct ListDevicesCommand: Command {
|
||||
let name = "list-devices"
|
||||
let overview = "List available Xcode Simulator devices"
|
||||
|
||||
private let binder = ArgumentBinder<ListDevicesCommand>()
|
||||
private var filteringOptions = FilteringOptions()
|
||||
|
||||
func addOptions(to parser: ArgumentParser) {
|
||||
binder.bind(option: parser.add(
|
||||
option: "--availability",
|
||||
kind: FilteringOptions.Availability.self,
|
||||
usage: "Only list available devices? yes|no|all, defaults to all"
|
||||
), to: { command, availability in
|
||||
command.filteringOptions.availability = availability
|
||||
})
|
||||
}
|
||||
|
||||
mutating func fillParseResult(_ parseResult: ArgumentParser.Result) throws {
|
||||
try binder.fill(parseResult: parseResult, into: &self)
|
||||
}
|
||||
|
||||
func run() throws {
|
||||
let allDevices = try Simctl.listDevices()
|
||||
for (runtime, devices) in allDevices.devices {
|
||||
let filteredDevices = devices.filter(using: filteringOptions)
|
||||
guard !filteredDevices.isEmpty else {
|
||||
continue
|
||||
}
|
||||
print("\(runtime):")
|
||||
for device in devices {
|
||||
for device in filteredDevices {
|
||||
print(" - \(device.name)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FilteringOptions.Availability {
|
||||
func matches(_ availability: Bool) -> Bool {
|
||||
switch (self, availability) {
|
||||
case (.yes, true), (.no, false), (.all, _):
|
||||
return true
|
||||
case (.yes, false), (.no, true):
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Sequence where Element == Simctl.Device {
|
||||
func filter(using filteringOptions: FilteringOptions) -> [Simctl.Device] {
|
||||
return filter { device in
|
||||
filteringOptions.availability.matches(device.isAvailable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ public class XcodeSimulatorTool {
|
|||
options.printUsage(on: stdoutStream)
|
||||
case .version:
|
||||
print("xcode-simulator-tool version 0.1")
|
||||
case .listDevices:
|
||||
try ListDevicesCommand().run()
|
||||
case .command(let command):
|
||||
try command.run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
|
||||
import XCTest
|
||||
import Basic
|
||||
import SPMUtility
|
||||
@testable import XcodeSimulatorKit
|
||||
|
||||
class CommandLineOptionsTests: XCTestCase {
|
||||
func testListDevices() throws {
|
||||
let options = try CommandLineOptions.parse(commandName: "command", arguments: ["list-devices"])
|
||||
XCTAssertEqual(options.subCommand, .listDevices)
|
||||
guard case let CommandLineOptions.SubCommand.command(command) = options.subCommand else {
|
||||
return XCTFail("Should be parsed as a command")
|
||||
}
|
||||
XCTAssertEqual(command.name, "list-devices")
|
||||
}
|
||||
|
||||
func testArgumentParser() throws {
|
||||
|
@ -18,4 +22,31 @@ class CommandLineOptionsTests: XCTestCase {
|
|||
let result = try parser.parse(["a"])
|
||||
XCTAssertEqual(result.subparser(parser), "a")
|
||||
}
|
||||
|
||||
func testSubParserWithOptions() throws {
|
||||
let parser = ArgumentParser(commandName: "SomeBinary", usage: "sample parser", overview: "Sample overview")
|
||||
let verboseOption = parser.add(
|
||||
option: "--verbose",
|
||||
shortName: "-v",
|
||||
kind: Bool.self,
|
||||
usage: "Print more information"
|
||||
)
|
||||
|
||||
let fillCupParser = parser.add(subparser: "fill-cup", overview: "Fill cup")
|
||||
let contentOption = fillCupParser.add(
|
||||
option: "--content",
|
||||
kind: String.self,
|
||||
usage: "Specify content to fill cup with"
|
||||
)
|
||||
|
||||
let result = try parser.parse(["--verbose", "fill-cup", "--content", "milk"])
|
||||
guard let foundSubparser = result.subparser(parser) else {
|
||||
return XCTFail("Expected to find a sub parser")
|
||||
}
|
||||
XCTAssertEqual(foundSubparser, "fill-cup")
|
||||
XCTAssertEqual(result.get(contentOption), "milk")
|
||||
|
||||
parser.printUsage(on: stdoutStream)
|
||||
fillCupParser.printUsage(on: stdoutStream)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue