parent
6561f04200
commit
3e727aacfa
|
@ -39,13 +39,13 @@ extension ExplorerXML {
|
|||
}
|
||||
|
||||
private static func fromArrayOfDictionaries(csv: CSV) throws -> ExplorerXML {
|
||||
let rootTree = Tree.root()
|
||||
let rootTree = Tree.root(name: "element")
|
||||
let headers = try csv.header
|
||||
.map { try (key: $0, path: Path(string: $0)) } // transform keys to paths
|
||||
.sorted { $0.path.comparedByKeyAndIndexes(with: $1.path) } // sort by path
|
||||
.map { ($0.key, rootTree.insert(path: $0.path)) } // insert paths in the tree
|
||||
|
||||
let explorer = ExplorerXML(name: Element.defaultName)
|
||||
let explorer = ExplorerXML(name: Element.root)
|
||||
|
||||
try csv.namedRows.forEach { row in
|
||||
let child = try from(row: row, with: headers, rootTree: rootTree)
|
||||
|
|
|
@ -8,6 +8,7 @@ import AEXML
|
|||
extension AEXMLElement {
|
||||
|
||||
static let defaultName = "element"
|
||||
static let root = "root"
|
||||
|
||||
func isEqual(to other: AEXMLElement) -> Bool {
|
||||
|
||||
|
|
|
@ -45,8 +45,8 @@ final class PathTree<Value: Equatable> {
|
|||
PathTree(value: .node(children: children), element: element)
|
||||
}
|
||||
|
||||
static func root() -> PathTree {
|
||||
.node(children: [], element: .key("root"))
|
||||
static func root(name: String = "root") -> PathTree {
|
||||
.node(children: [], element: .key(name))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ struct AddCommand: SADCommand {
|
|||
|
||||
// MARK: - Properties
|
||||
|
||||
@Option(name: DataFormat.name)
|
||||
@Option(name: .dataFormat, help: .dataFormat)
|
||||
var dataFormat: Scout.DataFormat
|
||||
|
||||
@Argument(help: PathAndValue.help)
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// Scout
|
||||
// Copyright (c) 2020-present Alexis Bridoux
|
||||
// MIT license, see LICENSE file for details
|
||||
|
||||
import Foundation
|
||||
import Scout
|
||||
import ArgumentParser
|
||||
import ScoutCLTCore
|
||||
|
||||
struct CSVCommand: ParsableCommand {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "csv",
|
||||
abstract: "Convert a CSV input in one of the supported format")
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
@Option(name: [.short, .long], help: "The separator used in the CSV input")
|
||||
var separator: String
|
||||
|
||||
@Option(name: .dataFormat, help: .dataFormat)
|
||||
var dataFormat: DataFormat
|
||||
|
||||
@Flag(help: "Indicates whether the CSV input has headers and thus should be treated like an array of dictionaries")
|
||||
var headers: Headers
|
||||
|
||||
@Option(name: .inputFilePath, help: .inputFilePath, completion: .file())
|
||||
var inputFilePath: String?
|
||||
|
||||
@Option(name: .outputFilePath, help: .outputFilePath, completion: .file())
|
||||
var outputFilePath: String?
|
||||
|
||||
@Flag(help: .colorise)
|
||||
var color = ColorFlag.color
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
func run() throws {
|
||||
let inputData = try readDataOrInputStream(from: inputFilePath)
|
||||
let inputString = try String(data: inputData, encoding: .utf8).unwrapOrThrow(.dataToString)
|
||||
guard let character = separator.first, separator.count == 1 else {
|
||||
throw RuntimeError.custom("Argument 'separator' should be a unique character")
|
||||
}
|
||||
|
||||
switch dataFormat {
|
||||
case .json:
|
||||
let json = try Json.fromCSV(string: inputString, separator: character, hasHeaders: headers == .headers)
|
||||
try handleOutput(with: json)
|
||||
|
||||
case .plist:
|
||||
let plist = try Plist.fromCSV(string: inputString, separator: character, hasHeaders: headers == .headers)
|
||||
try handleOutput(with: plist)
|
||||
|
||||
case .yaml:
|
||||
let yaml = try Yaml.fromCSV(string: inputString, separator: character, hasHeaders: headers == .headers)
|
||||
try handleOutput(with: yaml)
|
||||
|
||||
case .xml:
|
||||
let xml = try Xml.fromCSV(string: inputString, separator: character, hasHeaders: headers == .headers)
|
||||
try handleOutput(with: xml)
|
||||
}
|
||||
}
|
||||
|
||||
func handleOutput<P: SerializablePathExplorer>(with explorer: P) throws {
|
||||
if let filePath = outputFilePath {
|
||||
let data = try explorer.exportData()
|
||||
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
|
||||
|
||||
} else {
|
||||
var output = try explorer.exportString()
|
||||
let colorInjector = try self.colorInjector(for: dataFormat)
|
||||
output = colorise ? colorInjector.inject(in: output) : output
|
||||
print(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CSVCommand {
|
||||
|
||||
enum Headers: EnumerableFlag {
|
||||
case headers
|
||||
case noHeaders
|
||||
}
|
||||
}
|
||||
|
||||
extension CSVCommand {
|
||||
|
||||
/// Colorize the output
|
||||
var colorise: Bool {
|
||||
if color == .forceColor { return true }
|
||||
return color.colorise && !FileHandle.standardOutput.isPiped
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ struct DeleteCommand: SADCommand {
|
|||
|
||||
// MARK: - Properties
|
||||
|
||||
@Option(name: DataFormat.name)
|
||||
@Option(name: .dataFormat, help: .dataFormat)
|
||||
var dataFormat: Scout.DataFormat
|
||||
|
||||
@Argument(help: "Paths to indicate the keys to be deleted")
|
||||
|
|
|
@ -9,5 +9,4 @@ import ArgumentParser
|
|||
extension DataFormat: ExpressibleByArgument {
|
||||
|
||||
public var defaultValueDescription: String { "The data format to read the input" }
|
||||
static var name: NameSpecification = [.customShort("f", allowingJoined: true), .customLong("format")]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// Scout
|
||||
// Copyright (c) 2020-present Alexis Bridoux
|
||||
// MIT license, see LICENSE file for details
|
||||
|
||||
import Foundation
|
||||
import ArgumentParser
|
||||
import Scout
|
||||
import Lux
|
||||
|
||||
extension ParsableCommand {
|
||||
|
||||
/// Try to read data from the optional `filePath`. Otherwise, return the data from the standard input stream
|
||||
func readDataOrInputStream(from filePath: String?) throws -> Data {
|
||||
if let filePath = filePath {
|
||||
return try Data(contentsOf: URL(fileURLWithPath: filePath.replacingTilde))
|
||||
}
|
||||
|
||||
// The function `readDataToEndOfFile()` was deprecated since macOS 10.15.4
|
||||
// but now (macOS 11.2.2) it seems to be deprecated for never (100_000)).
|
||||
return FileHandle.standardInput.readDataToEndOfFile()
|
||||
|
||||
// return try input.data(using: .utf8)
|
||||
// .unwrapOrThrow(error: .invalidData("Unable to get data from standard input"))
|
||||
|
||||
// if #available(OSX 10.15.4, *) {
|
||||
// do {
|
||||
// return try FileHandle
|
||||
// .standardInput
|
||||
// .readToEnd()
|
||||
// .unwrapOrThrow(error: .invalidData("Unable to get data from standard input"))
|
||||
// }
|
||||
// return standardInputData
|
||||
// } catch {
|
||||
// throw RuntimeError.invalidData("Error while reading data from standard input. \(error.localizedDescription)")
|
||||
// }
|
||||
// } else {
|
||||
// return FileHandle.standardInput.readDataToEndOfFile()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
extension ParsableCommand {
|
||||
|
||||
func colorInjector(for format: Scout.DataFormat) throws -> TextInjector {
|
||||
switch format {
|
||||
|
||||
case .json:
|
||||
let jsonInjector = JSONInjector(type: .terminal)
|
||||
if let colors = try getColorFile()?.json {
|
||||
jsonInjector.delegate = JSONInjectorColorDelegate(colors: colors)
|
||||
}
|
||||
return jsonInjector
|
||||
|
||||
case .plist:
|
||||
let plistInjector = PlistInjector(type: .terminal)
|
||||
if let colors = try getColorFile()?.plist {
|
||||
plistInjector.delegate = PlistInjectorColorDelegate(colors: colors)
|
||||
}
|
||||
return plistInjector
|
||||
|
||||
case .yaml:
|
||||
let yamlInjector = YAMLInjector(type: .terminal)
|
||||
if let colors = try getColorFile()?.yaml {
|
||||
yamlInjector.delegate = YAMLInjectorColorDelegate(colors: colors)
|
||||
}
|
||||
return yamlInjector
|
||||
|
||||
case .xml:
|
||||
let xmlInjector = XMLEnhancedInjector(type: .terminal)
|
||||
if let colors = try getColorFile()?.xml {
|
||||
xmlInjector.delegate = XMLInjectorColorDelegate(colors: colors)
|
||||
}
|
||||
return xmlInjector
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the color file to colorise the output if one is found
|
||||
func getColorFile() throws -> ColorFile? {
|
||||
let colorFileURL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".scout/Colors.plist")
|
||||
guard let data = try? Data(contentsOf: colorFileURL) else { return nil }
|
||||
|
||||
return try PropertyListDecoder().decode(ColorFile.self, from: data)
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import ArgumentParser
|
|||
|
||||
extension NameSpecification {
|
||||
|
||||
static let dataFormat: NameSpecification = [.customShort("f", allowingJoined: true), .customLong("format")]
|
||||
static let inputFilePath: NameSpecification = [.short, .customLong("input")]
|
||||
static let outputFilePath: NameSpecification = [.short, .customLong("output")]
|
||||
static let modifyFilePath: NameSpecification = [.short, .customLong("modify")]
|
||||
|
@ -17,6 +18,7 @@ extension NameSpecification {
|
|||
|
||||
extension ArgumentHelp {
|
||||
|
||||
static let dataFormat = ArgumentHelp("The data format of the input")
|
||||
static let inputFilePath = ArgumentHelp("A file path from which to read the data")
|
||||
static let outputFilePath = ArgumentHelp("Write the modified data into the file at the given path")
|
||||
static let modifyFilePath = ArgumentHelp("Read and write the data into the same file at the given path")
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// Scout
|
||||
// Copyright (c) 2020-present Alexis Bridoux
|
||||
// MIT license, see LICENSE file for details
|
||||
|
||||
import Foundation
|
||||
import Scout
|
||||
import ArgumentParser
|
||||
|
||||
/// Try to get a PathExplorer from the input
|
||||
protocol PathExplorerInputCommand: ParsableCommand {
|
||||
|
||||
/// A file path from which to read the data
|
||||
var inputFilePath: String? { get }
|
||||
|
||||
/// A file path from which to read and write the data
|
||||
var modifyFilePath: String? { get }
|
||||
|
||||
var dataFormat: Scout.DataFormat { get }
|
||||
|
||||
/// Called with the correct `PathExplorer` when `inferPathExplorer(from:in:)` completes
|
||||
func inferred<P: SerializablePathExplorer>(pathExplorer: P) throws
|
||||
}
|
||||
|
||||
extension PathExplorerInputCommand {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var modifyFilePath: String? { nil }
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
func run() throws {
|
||||
var filePath: String?
|
||||
switch (inputFilePath?.replacingTilde, modifyFilePath?.replacingTilde) {
|
||||
case (.some(let path), nil): filePath = path
|
||||
case (nil, .some(let path)): filePath = path
|
||||
case (nil, nil): break
|
||||
case (.some, .some): throw RuntimeError.invalidArgumentsCombination(description: "Combining (-i|--input) with (-m|--modify) is not allowed")
|
||||
}
|
||||
|
||||
let data = try readDataOrInputStream(from: filePath)
|
||||
try makePathExplorer(for: dataFormat, from: data)
|
||||
}
|
||||
|
||||
private func makeExplorer<P: SerializablePathExplorer>(_ type: P.Type, from data: Data) throws -> P {
|
||||
do {
|
||||
return try P(data: data)
|
||||
} catch {
|
||||
throw RuntimeError.unknownFormat("The input cannot be read as \(dataFormat).\n\(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
func makePathExplorer(for dataFormat: Scout.DataFormat, from data: Data) throws {
|
||||
switch dataFormat {
|
||||
case .json:
|
||||
let json = try makeExplorer(Json.self, from: data)
|
||||
try inferred(pathExplorer: json)
|
||||
case .plist:
|
||||
let plist = try makeExplorer(Plist.self, from: data)
|
||||
try inferred(pathExplorer: plist)
|
||||
case .yaml:
|
||||
let yaml = try makeExplorer(Yaml.self, from: data)
|
||||
try inferred(pathExplorer: yaml)
|
||||
case .xml:
|
||||
let xml = try makeExplorer(Xml.self, from: data)
|
||||
try inferred(pathExplorer: xml)
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to get the regex from the pattern, throwing a `RuntimeError` when failing
|
||||
func regexFrom(pattern: String) throws -> NSRegularExpression {
|
||||
guard let regex = try? NSRegularExpression(pattern: pattern) else {
|
||||
throw RuntimeError.invalidRegex(pattern)
|
||||
}
|
||||
return regex
|
||||
}
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
//
|
||||
// Scout
|
||||
// Copyright (c) 2020-present Alexis Bridoux
|
||||
// MIT license, see LICENSE file for details
|
||||
|
||||
import Foundation
|
||||
import Scout
|
||||
import ArgumentParser
|
||||
import Lux
|
||||
|
||||
protocol ScoutCommand: ParsableCommand {
|
||||
|
||||
/// A file path from which to read the data
|
||||
var inputFilePath: String? { get }
|
||||
|
||||
/// A file path from which to read and write the data
|
||||
var modifyFilePath: String? { get }
|
||||
|
||||
var dataFormat: Scout.DataFormat { get }
|
||||
|
||||
/// Called with the correct `PathExplorer` when `inferPathExplorer(from:in:)` completes
|
||||
func inferred<P: SerializablePathExplorer>(pathExplorer: P) throws
|
||||
}
|
||||
|
||||
extension ScoutCommand {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var modifyFilePath: String? { nil }
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
func run() throws {
|
||||
var filePath: String?
|
||||
switch (inputFilePath?.replacingTilde, modifyFilePath?.replacingTilde) {
|
||||
case (.some(let path), nil): filePath = path
|
||||
case (nil, .some(let path)): filePath = path
|
||||
case (nil, nil): break
|
||||
case (.some, .some): throw RuntimeError.invalidArgumentsCombination(description: "Combining (-i|--input) with (-m|--modify) is not allowed")
|
||||
}
|
||||
|
||||
let data = try readDataOrInputStream(from: filePath)
|
||||
try makePathExplorer(for: dataFormat, from: data)
|
||||
}
|
||||
|
||||
/// Try to read data from the optional `filePath`. Otherwise, return the data from the standard input stream
|
||||
func readDataOrInputStream(from filePath: String?) throws -> Data {
|
||||
if let filePath = filePath {
|
||||
return try Data(contentsOf: URL(fileURLWithPath: filePath.replacingTilde))
|
||||
}
|
||||
|
||||
// The function `readDataToEndOfFile()` was deprecated since macOS 10.15.4
|
||||
// but now (macOS 11.2.2) it seems to be deprecated for never (100_000)).
|
||||
return FileHandle.standardInput.readDataToEndOfFile()
|
||||
|
||||
// return try input.data(using: .utf8)
|
||||
// .unwrapOrThrow(error: .invalidData("Unable to get data from standard input"))
|
||||
|
||||
// if #available(OSX 10.15.4, *) {
|
||||
// do {
|
||||
// return try FileHandle
|
||||
// .standardInput
|
||||
// .readToEnd()
|
||||
// .unwrapOrThrow(error: .invalidData("Unable to get data from standard input"))
|
||||
// }
|
||||
// return standardInputData
|
||||
// } catch {
|
||||
// throw RuntimeError.invalidData("Error while reading data from standard input. \(error.localizedDescription)")
|
||||
// }
|
||||
// } else {
|
||||
// return FileHandle.standardInput.readDataToEndOfFile()
|
||||
// }
|
||||
}
|
||||
|
||||
private func makeExplorer<P: SerializablePathExplorer>(_ type: P.Type, from data: Data) throws -> P {
|
||||
do {
|
||||
return try P(data: data)
|
||||
} catch {
|
||||
throw RuntimeError.unknownFormat("The input cannot be read as \(dataFormat).\n\(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
func makePathExplorer(for dataFormat: Scout.DataFormat, from data: Data) throws {
|
||||
switch dataFormat {
|
||||
case .json:
|
||||
let json = try makeExplorer(Json.self, from: data)
|
||||
try inferred(pathExplorer: json)
|
||||
case .plist:
|
||||
let plist = try makeExplorer(Plist.self, from: data)
|
||||
try inferred(pathExplorer: plist)
|
||||
case .yaml:
|
||||
let yaml = try makeExplorer(Yaml.self, from: data)
|
||||
try inferred(pathExplorer: yaml)
|
||||
case .xml:
|
||||
let xml = try makeExplorer(Xml.self, from: data)
|
||||
try inferred(pathExplorer: xml)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Useless with the new required dataFormat flag.")
|
||||
func inferPathExplorer(from data: Data, in inputFilePath: String?) throws {
|
||||
|
||||
if let json = try? Json(data: data) {
|
||||
try inferred(pathExplorer: json)
|
||||
} else if let plist = try? Plist(data: data) {
|
||||
try inferred(pathExplorer: plist)
|
||||
} else if let yaml = try? Yaml(data: data) {
|
||||
try inferred(pathExplorer: yaml)
|
||||
} else if let xml = try? Xml(data: data) {
|
||||
try inferred(pathExplorer: xml)
|
||||
} else {
|
||||
if let filePath = inputFilePath {
|
||||
throw RuntimeError.unknownFormat("The format of the file at \(filePath) is not recognized")
|
||||
} else {
|
||||
throw RuntimeError.unknownFormat("The format of the input stream is not recognized")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the color file to colorise the output if one is found
|
||||
func getColorFile() throws -> ColorFile? {
|
||||
let colorFileURL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".scout/Colors.plist")
|
||||
guard let data = try? Data(contentsOf: colorFileURL) else { return nil }
|
||||
|
||||
return try PropertyListDecoder().decode(ColorFile.self, from: data)
|
||||
}
|
||||
|
||||
func colorInjector(for format: Scout.DataFormat) throws -> TextInjector {
|
||||
switch format {
|
||||
|
||||
case .json:
|
||||
let jsonInjector = JSONInjector(type: .terminal)
|
||||
if let colors = try getColorFile()?.json {
|
||||
jsonInjector.delegate = JSONInjectorColorDelegate(colors: colors)
|
||||
}
|
||||
return jsonInjector
|
||||
|
||||
case .plist:
|
||||
let plistInjector = PlistInjector(type: .terminal)
|
||||
if let colors = try getColorFile()?.plist {
|
||||
plistInjector.delegate = PlistInjectorColorDelegate(colors: colors)
|
||||
}
|
||||
return plistInjector
|
||||
|
||||
case .yaml:
|
||||
let yamlInjector = YAMLInjector(type: .terminal)
|
||||
if let colors = try getColorFile()?.yaml {
|
||||
yamlInjector.delegate = YAMLInjectorColorDelegate(colors: colors)
|
||||
}
|
||||
return yamlInjector
|
||||
|
||||
case .xml:
|
||||
let xmlInjector = XMLEnhancedInjector(type: .terminal)
|
||||
if let colors = try getColorFile()?.xml {
|
||||
xmlInjector.delegate = XMLInjectorColorDelegate(colors: colors)
|
||||
}
|
||||
return xmlInjector
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to get the regex from the pattern, throwing a `RuntimeError` when failing
|
||||
func regexFrom(pattern: String) throws -> NSRegularExpression {
|
||||
guard let regex = try? NSRegularExpression(pattern: pattern) else {
|
||||
throw RuntimeError.invalidRegex(pattern)
|
||||
}
|
||||
return regex
|
||||
}
|
||||
}
|
|
@ -16,8 +16,8 @@ private let discussion =
|
|||
To find advanced help and rich examples, please type `scout doc`.
|
||||
|
||||
|
||||
Written by Alexis Bridoux.
|
||||
\u{001B}[38;5;88mhttps://github.com/ABridoux/scout\u{001B}[0;0m
|
||||
Written by Alexis Bridoux. Copyright (c) 2020-present.
|
||||
\u{001B}[38;5;88mhttps://www.woodys-findings.com/scout\u{001B}[0;0m
|
||||
MIT license, see LICENSE file for details
|
||||
"""
|
||||
|
||||
|
@ -37,7 +37,7 @@ struct ScoutMainCommand: ParsableCommand {
|
|||
AddCommand.self,
|
||||
DocCommand.self,
|
||||
PathsCommand.self,
|
||||
CSVCommand.self,
|
||||
InstallCompletionScriptCommand.self],
|
||||
defaultSubcommand: ReadCommand.self)
|
||||
|
||||
}
|
||||
|
|
|
@ -7,22 +7,26 @@ import Foundation
|
|||
|
||||
enum RuntimeError: LocalizedError {
|
||||
case invalidData(String)
|
||||
case dataToString
|
||||
case noValueAt(path: String)
|
||||
case unknownFormat(String)
|
||||
case completionScriptInstallation(description: String)
|
||||
case invalidRegex(String)
|
||||
case invalidArgumentsCombination(description: String)
|
||||
case valueConversion(value: String, type: String)
|
||||
case custom(String)
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .invalidData(let description): return description
|
||||
case .dataToString: return "The input data cannot be converted to UTF-8 String"
|
||||
case .noValueAt(let path): return "No value at '\(path)'"
|
||||
case .unknownFormat(let description): return description
|
||||
case .completionScriptInstallation(let description): return "Error while installing the completion script. \(description)"
|
||||
case .invalidRegex(let pattern): return "The regular expression '\(pattern)' is invalid"
|
||||
case .invalidArgumentsCombination(let description): return description
|
||||
case .valueConversion(let value, let type): return "The value \(value) is not convertible to \(type)"
|
||||
case .custom(let description): return description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import Lux
|
|||
import ScoutCLTCore
|
||||
|
||||
/// A Set/Add/Delete command for default implementations
|
||||
protocol SADCommand: ScoutCommand, ExportCommand {
|
||||
protocol SADCommand: PathExplorerInputCommand, ExportCommand {
|
||||
|
||||
associatedtype PathCollection: Collection
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import ArgumentParser
|
|||
|
||||
extension PathsFilter.ValueTarget: EnumerableFlag {}
|
||||
|
||||
struct PathsCommand: ScoutCommand {
|
||||
struct PathsCommand: PathExplorerInputCommand {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
|
@ -20,7 +20,7 @@ struct PathsCommand: ScoutCommand {
|
|||
|
||||
// MARK: - Properties
|
||||
|
||||
@Option(name: DataFormat.name)
|
||||
@Option(name: .dataFormat, help: .dataFormat)
|
||||
var dataFormat: Scout.DataFormat
|
||||
|
||||
@Argument(help: "Initial path from which the paths should be listed")
|
||||
|
|
|
@ -9,7 +9,7 @@ import Foundation
|
|||
import Lux
|
||||
import ScoutCLTCore
|
||||
|
||||
struct ReadCommand: ScoutCommand, ExportCommand {
|
||||
struct ReadCommand: PathExplorerInputCommand, ExportCommand {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
|
@ -30,7 +30,7 @@ struct ReadCommand: ScoutCommand, ExportCommand {
|
|||
&& !FileHandle.standardOutput.isPiped
|
||||
}
|
||||
|
||||
@Option(name: DataFormat.name)
|
||||
@Option(name: .dataFormat, help: .dataFormat)
|
||||
var dataFormat: Scout.DataFormat
|
||||
|
||||
@Argument(help: .readingPath)
|
||||
|
|
|
@ -17,7 +17,7 @@ struct SetCommand: SADCommand {
|
|||
|
||||
// MARK: - Properties
|
||||
|
||||
@Option(name: DataFormat.name)
|
||||
@Option(name: .dataFormat, help: .dataFormat)
|
||||
var dataFormat: Scout.DataFormat
|
||||
|
||||
@Argument(help: PathAndValue.help)
|
||||
|
|
Loading…
Reference in New Issue