scout/Sources/ScoutCLT/Models/SADCommand.swift

136 lines
4.4 KiB
Swift

//
// Scout
// Copyright (c) 2020-present Alexis Bridoux
// MIT license, see LICENSE file for details
import Scout
import ArgumentParser
import Foundation
import Lux
import ScoutCLTCore
/// A Set/Add/Delete command for default implementations
protocol SADCommand: PathExplorerInputCommand, ExportCommand {
associatedtype PathCollection: Collection
var pathsCollection: PathCollection { get }
var color: ColorFlag { get }
var level: Int? { get }
var modifyFilePath: String? { get }
var outputFilePath: String? { get }
/// Executed for each `pathsCollection` element
func perform<P: SerializablePathExplorer>(pathExplorer: inout P, pathAndValue: PathCollection.Element) throws
}
extension SADCommand {
/// Colorize the output
var colorise: Bool {
if color == .forceColor { return true }
return
color.colorise
&& csvSeparator == nil
&& !FileHandle.standardOutput.isPiped
}
func inferred<P: SerializablePathExplorer>(pathExplorer: P) throws {
let outputPath = modifyFilePath?.replacingTilde ?? outputFilePath?.replacingTilde
var explorer = pathExplorer
try pathsCollection.forEach { try perform(pathExplorer: &explorer, pathAndValue: $0) }
try printOutput(outputPath, dataWith: explorer, colorise: colorise, level: level)
}
}
extension SADCommand {
/// Print the data from the path explorer and highlight it if specified
/// - Parameters:
/// - outputFilePath: A file path to a file where to write the data
/// - pathExplorer: The path explorer to use to get the data
/// - colorise: `true` if the data should be colorised
/// - level: The level to fold the data
/// - csvSeparator: The csv separator to use to export the data
func printOutput<P: SerializablePathExplorer>(_ outputFilePath: String?, dataWith pathExplorer: P, colorise: Bool, level: Int? = nil) throws {
if let output = outputFilePath?.replacingTilde {
FileManager.default.createFile(atPath: output, contents: nil, attributes: nil)
}
switch try exportOption() {
case .csv(let separator):
let csv = try pathExplorer.exportCSV(separator: separator)
if let output = outputFilePath {
try csv.write(toFile: output, atomically: false, encoding: .utf8)
} else {
print(csv)
}
return
case .dataFormat(let format):
let exported = try pathExplorer.exportData(to: format, rootName: fileName(of: inputFilePath))
if let output = outputFilePath {
try exported.write(to: URL(fileURLWithPath: output))
} else {
guard let string = String(data: exported, encoding: .utf8) else {
throw SerializationError.dataToString
}
try printOutput(output: string, with: format)
}
return
case .array:
do {
let array = try pathExplorer.array(of: GroupExportValue.self).map(\.value).joined(separator: " ")
print("\(array)")
} catch {
throw RuntimeError.custom("Unable to represent the value as an array of single elements")
}
case .dictionary:
do {
let dict = try pathExplorer.dictionary(of: GroupExportValue.self)
.map { "\($0.key) \($0.value.value)" }
.joined(separator: " ")
print("\(dict)")
} catch {
throw RuntimeError.custom("Unable to represent the value as a dictionary of single elements")
}
case .noExport:
break
}
// fold if specified
let output: String
if let level = level {
output = try pathExplorer.exportFoldedString(upTo: level)
} else {
output = try pathExplorer.exportString()
}
if let filePath = outputFilePath {
try output.write(toFile: filePath, atomically: false, encoding: .utf8)
} else {
try printOutput(output: output, with: P.format)
}
}
func printOutput(output: String, with format: Scout.DataFormat) throws {
if colorise {
let highlight = try colorInjector(for: format)
print(highlight(output))
} else {
print(output)
}
}
}