Merge pull request #17 from tuist/add-json-support-to-get-and-list

Add `--json` support to get and list
This commit is contained in:
f-trycua
2025-02-09 18:29:49 +01:00
committed by GitHub
5 changed files with 99 additions and 11 deletions

View File

@@ -9,6 +9,9 @@ struct Get: AsyncParsableCommand {
@Argument(help: "Name of the virtual machine", completion: .custom(completeVMName))
var name: String
@Option(name: [.long, .customShort("f")], help: "Output format (json|text)")
var format: FormatOption = .text
init() {
}
@@ -16,6 +19,6 @@ struct Get: AsyncParsableCommand {
func run() async throws {
let vmController = LumeController()
let vm = try vmController.get(name: name)
VMDetailsPrinter.printStatus([vm.details])
try VMDetailsPrinter.printStatus([vm.details], format: self.format)
}
}
}

View File

@@ -7,6 +7,9 @@ struct List: AsyncParsableCommand {
abstract: "List virtual machines"
)
@Option(name: [.long, .customShort("f")], help: "Output format (json|text)")
var format: FormatOption = .text
init() {
}
@@ -14,10 +17,10 @@ struct List: AsyncParsableCommand {
func run() async throws {
let manager = LumeController()
let vms = try manager.list()
if vms.isEmpty {
if vms.isEmpty && self.format == .text {
print("No virtual machines found")
} else {
VMDetailsPrinter.printStatus(vms)
try VMDetailsPrinter.printStatus(vms, format: self.format)
}
}
}
}

View File

@@ -0,0 +1,6 @@
import ArgumentParser
enum FormatOption: String, ExpressibleByArgument {
case json
case text
}

View File

@@ -34,17 +34,25 @@ enum VMDetailsPrinter {
/// Prints the status of all VMs in a formatted table
/// - Parameter vms: Array of VM status objects to display
static func printStatus(_ vms: [VMDetails]) {
printHeader()
vms.forEach(printVM)
static func printStatus(_ vms: [VMDetails], format: FormatOption, print: (String) -> () = { print($0) }) throws {
if format == .json {
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
let jsonData = try jsonEncoder.encode(vms)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
} else {
printHeader(print: print)
vms.forEach({ printVM($0, print: print)})
}
}
private static func printHeader() {
private static func printHeader(print: (String) -> () = { print($0) }) {
let paddedHeaders = columns.map { $0.header.paddedToWidth($0.width) }
print(paddedHeaders.joined())
}
private static func printVM(_ vm: VMDetails) {
private static func printVM(_ vm: VMDetails, print: (String) -> Void = { print($0) }) {
let paddedColumns = columns.map { column in
column.getValue(vm).paddedToWidth(column.width)
}
@@ -59,4 +67,4 @@ private extension String {
func paddedToWidth(_ width: Int) -> String {
padding(toLength: width, withPad: " ", startingAt: 0)
}
}
}

View File

@@ -0,0 +1,68 @@
import Testing
import Foundation
@testable import lume
struct VMDetailsPrinterTests {
@Test func printStatus_whenJSON() throws {
// Given
let vms: [VMDetails] = [VMDetails(name: "name",
os: "os",
cpuCount: 2,
memorySize: 1024,
diskSize: .init(allocated: 24, total: 30),
status: "status",
vncUrl: "vncUrl",
ipAddress: "0.0.0.0")]
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
let expectedOutput = try String(data: jsonEncoder.encode(vms), encoding: .utf8)!
// When
var printedStatus: String?
try VMDetailsPrinter.printStatus(vms, format: .json, print: { printedStatus = $0 })
// Then
// Decode both JSONs and compare the actual data structures
let jsonDecoder = JSONDecoder()
let printedVMs = try jsonDecoder.decode([VMDetails].self, from: printedStatus!.data(using: .utf8)!)
let expectedVMs = try jsonDecoder.decode([VMDetails].self, from: expectedOutput.data(using: .utf8)!)
#expect(printedVMs.count == expectedVMs.count)
for (printed, expected) in zip(printedVMs, expectedVMs) {
#expect(printed.name == expected.name)
#expect(printed.os == expected.os)
#expect(printed.cpuCount == expected.cpuCount)
#expect(printed.memorySize == expected.memorySize)
#expect(printed.diskSize.allocated == expected.diskSize.allocated)
#expect(printed.diskSize.total == expected.diskSize.total)
#expect(printed.status == expected.status)
#expect(printed.vncUrl == expected.vncUrl)
#expect(printed.ipAddress == expected.ipAddress)
}
}
@Test func printStatus_whenNotJSON() throws {
// Given
let vms: [VMDetails] = [VMDetails(name: "name",
os: "os",
cpuCount: 2,
memorySize: 1024,
diskSize: .init(allocated: 24, total: 30),
status: "status",
vncUrl: "vncUrl",
ipAddress: "0.0.0.0")]
// When
var printedLines: [String] = []
try VMDetailsPrinter.printStatus(vms, format: .text, print: { printedLines.append($0) })
// Then
#expect(printedLines.count == 2)
let headerParts = printedLines[0].split(whereSeparator: \.isWhitespace)
#expect(headerParts == ["name", "os", "cpu", "memory", "disk", "status", "ip", "vnc"])
#expect(printedLines[1].split(whereSeparator: \.isWhitespace).map(String.init) == ["name", "os", "2", "0.00G", "24.0B/30.0B", "status", "0.0.0.0", "vncUrl"])
}
}