import Commandant import Result import SwiftLintFramework struct AutoCorrectCommand: CommandProtocol { let verb = "autocorrect" let function = "Automatically correct warnings and errors" func run(_ options: AutoCorrectOptions) -> Result<(), CommandantError<()>> { let configuration = Configuration(options: options) let visitor = options.visitor(with: configuration) return configuration.visitLintableFiles(with: visitor).flatMap { files in if !options.quiet { let pluralSuffix = { (collection: [Any]) -> String in return collection.count != 1 ? "s" : "" } queuedPrintError("Done correcting \(files.count) file\(pluralSuffix(files))!") } return .success(()) } } } struct AutoCorrectOptions: OptionsProtocol { let paths: [String] let configurationFile: String let useScriptInputFiles: Bool let quiet: Bool let forceExclude: Bool let format: Bool let cachePath: String let ignoreCache: Bool // swiftlint:disable line_length static func create(_ path: String) -> (_ configurationFile: String) -> (_ useScriptInputFiles: Bool) -> (_ quiet: Bool) -> (_ forceExclude: Bool) -> (_ format: Bool) -> (_ cachePath: String) -> (_ ignoreCache: Bool) -> (_ paths: [String]) -> AutoCorrectOptions { return { configurationFile in { useScriptInputFiles in { quiet in { forceExclude in { format in { cachePath in { ignoreCache in { paths in let allPaths: [String] if !path.isEmpty { allPaths = [path] } else { allPaths = paths } return self.init(paths: allPaths, configurationFile: configurationFile, useScriptInputFiles: useScriptInputFiles, quiet: quiet, forceExclude: forceExclude, format: format, cachePath: cachePath, ignoreCache: ignoreCache) // swiftlint:enable line_length }}}}}}}} } static func evaluate(_ mode: CommandMode) -> Result>> { return create <*> mode <| pathOption(action: "correct") <*> mode <| configOption <*> mode <| useScriptInputFilesOption <*> mode <| quietOption(action: "correcting") <*> mode <| Option(key: "force-exclude", defaultValue: false, usage: "exclude files in config `excluded` even if their paths are explicitly specified") <*> mode <| Option(key: "format", defaultValue: false, usage: "should reformat the Swift files") <*> mode <| Option(key: "cache-path", defaultValue: "", usage: "the directory of the cache used when correcting") <*> mode <| Option(key: "no-cache", defaultValue: false, usage: "ignore cache when correcting") // This should go last to avoid eating other args <*> mode <| pathsArgument(action: "correct") } fileprivate func visitor(with configuration: Configuration) -> LintableFilesVisitor { let cache = ignoreCache ? nil : LinterCache(configuration: configuration) return LintableFilesVisitor(paths: paths, action: "Correcting", useSTDIN: false, quiet: quiet, useScriptInputFiles: useScriptInputFiles, forceExclude: forceExclude, cache: cache, parallel: true) { linter in if self.format { switch configuration.indentation { case .tabs: linter.format(useTabs: true, indentWidth: 4) case .spaces(let count): linter.format(useTabs: false, indentWidth: count) } } let corrections = linter.correct() if !corrections.isEmpty && !self.quiet { let correctionLogs = corrections.map({ $0.consoleDescription }) queuedPrint(correctionLogs.joined(separator: "\n")) } } } }