Create logger.pretty and use PrintableString
This commit is contained in:
parent
f0871503da
commit
489b5b66b1
|
@ -67,13 +67,13 @@ final class BundleCommand: Command {
|
|||
}
|
||||
|
||||
let version = try String(contentsOf: versionFilePath.url)
|
||||
logger.info("Bundling the version \(version) in the directory \(binFolderPath.pathString)".section())
|
||||
logger.notice("Bundling the version \(version) in the directory \(binFolderPath.pathString)".section())
|
||||
|
||||
let versionPath = versionsController.path(version: version)
|
||||
|
||||
// Installing
|
||||
if !FileHandler.shared.exists(versionPath) {
|
||||
logger.info("Version \(version) not available locally. Installing...")
|
||||
logger.notice("Version \(version) not available locally. Installing...")
|
||||
try installer.install(version: version, force: false)
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,6 @@ final class BundleCommand: Command {
|
|||
}
|
||||
try FileHandler.shared.copy(from: versionPath, to: binFolderPath)
|
||||
|
||||
logger.info("tuist bundled successfully at \(binFolderPath.pathString)".success())
|
||||
logger.notice("tuist bundled successfully at \(binFolderPath.pathString)".success())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,9 +63,9 @@ class CommandRunner: CommandRunning {
|
|||
|
||||
switch resolvedVersion {
|
||||
case let .bin(path):
|
||||
logger.info("Using bundled version at path \(path.pathString)")
|
||||
logger.notice("Using bundled version at path \(path.pathString)")
|
||||
case let .versionFile(path, value):
|
||||
logger.info("Using version \(value) defined at \(path.pathString)")
|
||||
logger.notice("Using version \(value) defined at \(path.pathString)")
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ class CommandRunner: CommandRunning {
|
|||
|
||||
func runVersion(_ version: String) throws {
|
||||
if !versionsController.versions().contains(where: { $0.description == version }) {
|
||||
logger.info("Version \(version) not found locally. Installing...")
|
||||
logger.notice("Version \(version) not found locally. Installing...")
|
||||
try installer.install(version: version, force: false)
|
||||
}
|
||||
|
||||
|
|
|
@ -46,19 +46,19 @@ class LocalCommand: Command {
|
|||
// MARK: - Fileprivate
|
||||
|
||||
private func printLocalVersions() throws {
|
||||
logger.info("The following versions are available in the local environment:".section())
|
||||
logger.notice("The following versions are available in the local environment:".section())
|
||||
let versions = versionController.semverVersions()
|
||||
let output = versions.sorted().reversed().map { "- \($0)" }.joined(separator: "\n")
|
||||
logger.info("\(output)")
|
||||
logger.notice("\(output)")
|
||||
}
|
||||
|
||||
private func createVersionFile(version: String) throws {
|
||||
let currentPath = FileHandler.shared.currentPath
|
||||
logger.info("Generating \(Constants.versionFileName) file with version \(version)".section())
|
||||
logger.notice("Generating \(Constants.versionFileName) file with version \(version)".section())
|
||||
let tuistVersionPath = currentPath.appending(component: Constants.versionFileName)
|
||||
try "\(version)".write(to: URL(fileURLWithPath: tuistVersionPath.pathString),
|
||||
atomically: true,
|
||||
encoding: .utf8)
|
||||
logger.info("File generated at path \(tuistVersionPath.pathString)".success())
|
||||
logger.notice("File generated at path \(tuistVersionPath.pathString)".success())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ final class UninstallCommand: Command {
|
|||
let versions = versionsController.versions().map { $0.description }
|
||||
if versions.contains(version) {
|
||||
try versionsController.uninstall(version: version)
|
||||
logger.info("Version \(version) uninstalled".success())
|
||||
logger.notice("Version \(version) uninstalled".success())
|
||||
} else {
|
||||
logger.warning("Version \(version) cannot be uninstalled because it's not installed")
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ final class UpdateCommand: Command {
|
|||
/// - Throws: An error if the update process fails.
|
||||
func run(with result: ArgumentParser.Result) throws {
|
||||
let force = result.get(forceArgument) ?? false
|
||||
logger.info("Checking for updates...".section())
|
||||
logger.notice("Checking for updates...".section())
|
||||
try updater.update(force: force)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,6 @@ class VersionCommand: NSObject, Command {
|
|||
// MARK: - Command
|
||||
|
||||
func run(with _: ArgumentParser.Result) {
|
||||
logger.info("\(Constants.version)")
|
||||
logger.notice("\(Constants.version)")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ final class Installer: Installing {
|
|||
func install(version: String, temporaryDirectory: TemporaryDirectory, force: Bool = false) throws {
|
||||
// We ignore the Swift version and install from the soruce code
|
||||
if force {
|
||||
logger.info("Forcing the installation of \(version) from the source code")
|
||||
logger.notice("Forcing the installation of \(version) from the source code")
|
||||
try installFromSource(version: version,
|
||||
temporaryDirectory: temporaryDirectory)
|
||||
return
|
||||
|
@ -102,17 +102,17 @@ final class Installer: Installing {
|
|||
try versionsController.install(version: version, installation: { installationDirectory in
|
||||
|
||||
// Download bundle
|
||||
logger.info("Downloading version \(version)")
|
||||
logger.notice("Downloading version \(version)")
|
||||
|
||||
let downloadPath = temporaryDirectory.path.appending(component: Constants.bundleName)
|
||||
try System.shared.run("/usr/bin/curl", "-LSs", "--output", downloadPath.pathString, bundleURL.absoluteString)
|
||||
|
||||
// Unzip
|
||||
logger.info("Installing...")
|
||||
logger.notice("Installing...")
|
||||
try System.shared.run("/usr/bin/unzip", "-q", downloadPath.pathString, "-d", installationDirectory.pathString)
|
||||
|
||||
try createTuistVersionFile(version: version, path: installationDirectory)
|
||||
logger.info("Version \(version) installed")
|
||||
logger.notice("Version \(version) installed")
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ final class Installer: Installing {
|
|||
let buildDirectory = temporaryDirectory.path.appending(RelativePath(".build/release/"))
|
||||
|
||||
// Cloning and building
|
||||
logger.info("Pulling source code")
|
||||
logger.notice("Pulling source code")
|
||||
_ = try System.shared.observable(["/usr/bin/env", "git", "clone", Constants.gitRepositoryURL, temporaryDirectory.path.pathString])
|
||||
.mapToString()
|
||||
.printStandardError()
|
||||
|
@ -144,7 +144,7 @@ final class Installer: Installing {
|
|||
}
|
||||
}
|
||||
|
||||
logger.info("Building using Swift (it might take a while)")
|
||||
logger.notice("Building using Swift (it might take a while)")
|
||||
let swiftPath = try System.shared
|
||||
.observable(["/usr/bin/xcrun", "-f", "swift"])
|
||||
.mapToString()
|
||||
|
@ -185,7 +185,7 @@ final class Installer: Installing {
|
|||
to: installationDirectory)
|
||||
|
||||
try createTuistVersionFile(version: version, path: installationDirectory)
|
||||
logger.info("Version \(version) installed")
|
||||
logger.notice("Version \(version) installed")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,17 +39,17 @@ final class Updater: Updating {
|
|||
}
|
||||
|
||||
if force {
|
||||
logger.info("Forcing the update of version \(highestRemoteVersion)")
|
||||
logger.notice("Forcing the update of version \(highestRemoteVersion)")
|
||||
try installer.install(version: highestRemoteVersion.description, force: true)
|
||||
} else if let highestLocalVersion = versionsController.semverVersions().sorted().last {
|
||||
if highestRemoteVersion <= highestLocalVersion {
|
||||
logger.info("There are no updates available")
|
||||
logger.notice("There are no updates available")
|
||||
} else {
|
||||
logger.info("Installing new version available \(highestRemoteVersion)")
|
||||
logger.notice("Installing new version available \(highestRemoteVersion)")
|
||||
try installer.install(version: highestRemoteVersion.description, force: false)
|
||||
}
|
||||
} else {
|
||||
logger.info("No local versions available. Installing the latest version \(highestRemoteVersion)")
|
||||
logger.notice("No local versions available. Installing the latest version \(highestRemoteVersion)")
|
||||
try installer.install(version: highestRemoteVersion.description, force: false)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ final class ProjectGenerator: ProjectGenerating {
|
|||
sourceRootPath: AbsolutePath? = nil,
|
||||
xcodeprojPath: AbsolutePath? = nil) throws -> GeneratedProject {
|
||||
|
||||
logger.info("Generating project \(project.name)")
|
||||
logger.notice("Generating project \(project.name)")
|
||||
|
||||
// Getting the path.
|
||||
let sourceRootPath = sourceRootPath ?? project.path
|
||||
|
|
|
@ -88,7 +88,7 @@ final class WorkspaceGenerator: WorkspaceGenerating {
|
|||
tuistConfig _: TuistConfig) throws -> AbsolutePath {
|
||||
let workspaceName = "\(graph.name).xcworkspace"
|
||||
|
||||
logger.info("Generating workspace \(workspaceName)", metadata: .section)
|
||||
logger.notice("Generating workspace \(workspaceName)", metadata: .section)
|
||||
|
||||
/// Projects
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ final class CocoaPodsInteractor: CocoaPodsInteracting {
|
|||
|
||||
// The installation of Pods might fail if the local repository that contains the specs
|
||||
// is outdated.
|
||||
logger.info("Installing CocoaPods dependencies defined in \(node.podfilePath)", metadata: .section)
|
||||
logger.notice("Installing CocoaPods dependencies defined in \(node.podfilePath)", metadata: .section)
|
||||
|
||||
var mightNeedRepoUpdate: Bool = false
|
||||
let outputClosure: ([UInt8]) -> Void = { bytes in
|
||||
|
|
|
@ -62,7 +62,7 @@ final class CacheController: CacheControlling {
|
|||
try graphContentHasher.contentHashes(for: graph)
|
||||
.filter { target, hash in
|
||||
if let exists = try self.cache.exists(hash: hash).toBlocking().first(), exists {
|
||||
logger.notice("The target \(target.name, .highlight) with hash \(hash, .highlight) is already in the cache. Skipping...")
|
||||
logger.pretty("The target \(.bold(.raw(target.name))) with hash \(.bold(.raw(hash))) is already in the cache. Skipping...")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -44,6 +44,6 @@ class DumpCommand: NSObject, Command {
|
|||
}
|
||||
let project = try manifestLoader.loadProject(at: path)
|
||||
let json: JSON = try project.toJSON()
|
||||
logger.info("\(json.toString(prettyPrint: true))")
|
||||
logger.notice("\(json.toString(prettyPrint: true))")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,10 +52,10 @@ class EditCommand: NSObject, Command {
|
|||
try! FileHandler.shared.delete(EditCommand.temporaryDirectory.path)
|
||||
exit(0)
|
||||
}
|
||||
logger.info("Opening Xcode to edit the project. Press \("CTRL + C", .keystroke) once you are done editing")
|
||||
logger.pretty("Opening Xcode to edit the project. Press \(.keystroke("CTRL + C")) once you are done editing")
|
||||
try opener.open(path: xcodeprojPath, wait: true)
|
||||
} else {
|
||||
logger.info("Xcode project generated at \(xcodeprojPath.pathString)", metadata: .success)
|
||||
logger.notice("Xcode project generated at \(xcodeprojPath.pathString)", metadata: .success)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,8 +63,8 @@ class GenerateCommand: NSObject, Command {
|
|||
|
||||
let time = String(format: "%.3f", timer.stop())
|
||||
|
||||
logger.info("Project generated.", metadata: .success)
|
||||
logger.info("Total time taken: \(time)s")
|
||||
logger.notice("Project generated.", metadata: .success)
|
||||
logger.notice("Total time taken: \(time)s")
|
||||
}
|
||||
|
||||
// MARK: - Fileprivate
|
||||
|
|
|
@ -45,11 +45,11 @@ class GraphCommand: NSObject, Command {
|
|||
|
||||
let path = FileHandler.shared.currentPath.appending(component: "graph.dot")
|
||||
if FileHandler.shared.exists(path) {
|
||||
logger.info("Deleting existing graph at \(path.pathString)")
|
||||
logger.notice("Deleting existing graph at \(path.pathString)")
|
||||
try FileHandler.shared.delete(path)
|
||||
}
|
||||
|
||||
try FileHandler.shared.write(graph, path: path, atomically: true)
|
||||
logger.info("Graph exported to \(path.pathString)", metadata: .success)
|
||||
logger.notice("Graph exported to \(path.pathString)", metadata: .success)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ class InitCommand: NSObject, Command {
|
|||
try generateTuistConfig(path: path)
|
||||
try generateGitIgnore(path: path)
|
||||
|
||||
logger.info("Project generated at path \(path.pathString).", metadata: .success)
|
||||
logger.notice("Project generated at path \(path.pathString).", metadata: .success)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,6 @@ class VersionCommand: NSObject, Command {
|
|||
// MARK: - Command
|
||||
|
||||
func run(with _: ArgumentParser.Result) {
|
||||
logger.info("\(Constants.version)")
|
||||
logger.notice("\(Constants.version)")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public class SetupLoader: SetupLoading {
|
|||
.printAndThrowIfNeeded()
|
||||
try setup.forEach { command in
|
||||
if try !command.isMet(projectPath: path) {
|
||||
logger.info("Configuring \(command.name, .command)", metadata: .subsection)
|
||||
logger.notice("Configuring \(command.name)", metadata: .subsection)
|
||||
try command.meet(projectPath: path)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class UpHomebrew: Up, GraphInitiatable {
|
|||
/// - Throws: An error if any error is thrown while running it.
|
||||
override func meet(projectPath _: AbsolutePath) throws {
|
||||
if !toolInstalled("brew") {
|
||||
logger.info("Installing Homebrew")
|
||||
logger.notice("Installing Homebrew")
|
||||
try System.shared.runAndPrint("/usr/bin/ruby",
|
||||
"-e",
|
||||
"\"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"",
|
||||
|
@ -54,7 +54,7 @@ class UpHomebrew: Up, GraphInitiatable {
|
|||
}
|
||||
let nonInstalledPackages = packages.filter { !toolInstalled($0) }
|
||||
try nonInstalledPackages.forEach { package in
|
||||
logger.info("Installing Homebrew package: \(package)")
|
||||
logger.notice("Installing Homebrew package: \(package)")
|
||||
try System.shared.runAndPrint("/usr/local/bin/brew", "install", package,
|
||||
verbose: true,
|
||||
environment: System.shared.env)
|
||||
|
|
|
@ -62,7 +62,7 @@ class UpHomebrewTap: Up, GraphInitiatable {
|
|||
let taps = try self.taps()
|
||||
let notConfigured = repositories.filter { !isTapConfigured($0, taps: taps) }
|
||||
for repository in notConfigured {
|
||||
logger.info("Adding repository tap: \(repository)")
|
||||
logger.notice("Adding repository tap: \(repository)")
|
||||
try System.shared.run(["brew", "tap", repository])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,218 +0,0 @@
|
|||
public enum ConsolePrettyToken: String {
|
||||
|
||||
case highlight
|
||||
case command
|
||||
case keystroke
|
||||
|
||||
var tokens: Set<ConsoleToken> {
|
||||
switch self {
|
||||
case .highlight:
|
||||
return [ .bold ]
|
||||
case .command:
|
||||
return [ .white, .bold ]
|
||||
case .keystroke:
|
||||
return [ .cyan ]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Logger.Metadata {
|
||||
|
||||
public static let colored: String = "is"
|
||||
|
||||
public static let successKey: String = "success"
|
||||
public static var success: Logger.Metadata {
|
||||
return [ colored: .string(successKey) ]
|
||||
}
|
||||
|
||||
public static let sectionKey: String = "section"
|
||||
public static var section: Logger.Metadata {
|
||||
return [ colored: .string(sectionKey) ]
|
||||
}
|
||||
|
||||
public static let subsectionKey: String = "subsection"
|
||||
public static var subsection: Logger.Metadata {
|
||||
return [ colored: .string(subsectionKey) ]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension String.StringInterpolation {
|
||||
|
||||
public mutating func appendInterpolation(_ value: String, _ first: ConsolePrettyToken, rest: ConsolePrettyToken...) {
|
||||
appendInterpolation(value, [ first ] + rest)
|
||||
}
|
||||
|
||||
public mutating func appendInterpolation(_ value: String, _ token: [ConsolePrettyToken]) {
|
||||
let tokens = token.flatMap({ $0.tokens })
|
||||
appendInterpolation(value, Set(tokens))
|
||||
}
|
||||
|
||||
internal mutating func appendInterpolation(_ value: String, _ token: Set<ConsoleToken>) {
|
||||
|
||||
if Environment.shared.shouldOutputBeColoured {
|
||||
appendLiteral(value.apply(token))
|
||||
} else {
|
||||
appendLiteral(value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Set where Element == ConsoleToken {
|
||||
func apply(to string: String) -> String {
|
||||
reduce(string) { $1.apply($0) }
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func apply(_ token: Set<ConsoleToken>) -> String {
|
||||
token.apply(to: self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A token used in the console to colourize output. This OptionSet is used to
|
||||
/// allow for consumers to specify one-or-many different tokens to apply to the
|
||||
/// formatting of a string rendered inside the console. See ColourizeSwift for
|
||||
/// implementation details on how this unfolds when it arrives in Terminal.app.
|
||||
enum ConsoleToken: String {
|
||||
case black
|
||||
case blue
|
||||
case cyan
|
||||
case darkGray
|
||||
case green
|
||||
case lightBlue
|
||||
case lightCyan
|
||||
case lightGray
|
||||
case lightGreen
|
||||
case lightMagenta
|
||||
case lightRed
|
||||
case lightYellow
|
||||
case magenta
|
||||
case onBlack
|
||||
case onBlue
|
||||
case onCyan
|
||||
case onDarkGray
|
||||
case onGreen
|
||||
case onLightBlue
|
||||
case onLightCyan
|
||||
case onLightGray
|
||||
case onLightGreen
|
||||
case onLightMagenta
|
||||
case onLightRed
|
||||
case onLightYellow
|
||||
case onMagenta
|
||||
case onRed
|
||||
case onWhite
|
||||
case onYellow
|
||||
case red
|
||||
case white
|
||||
case yellow
|
||||
|
||||
case blink
|
||||
case bold
|
||||
case dim
|
||||
case hidden
|
||||
case italic
|
||||
case reset
|
||||
case reverse
|
||||
case strikethrough
|
||||
case underline
|
||||
|
||||
var apply: (String) -> String {
|
||||
|
||||
let ƒ: (String) -> () -> String
|
||||
|
||||
switch self {
|
||||
case .black:
|
||||
ƒ = String.black
|
||||
case .blue:
|
||||
ƒ = String.blue
|
||||
case .cyan:
|
||||
ƒ = String.cyan
|
||||
case .darkGray:
|
||||
ƒ = String.darkGray
|
||||
case .green:
|
||||
ƒ = String.green
|
||||
case .lightBlue:
|
||||
ƒ = String.lightBlue
|
||||
case .lightCyan:
|
||||
ƒ = String.lightCyan
|
||||
case .lightGray:
|
||||
ƒ = String.lightGray
|
||||
case .lightGreen:
|
||||
ƒ = String.lightGreen
|
||||
case .lightMagenta:
|
||||
ƒ = String.lightMagenta
|
||||
case .lightRed:
|
||||
ƒ = String.lightRed
|
||||
case .lightYellow:
|
||||
ƒ = String.lightYellow
|
||||
case .magenta:
|
||||
ƒ = String.magenta
|
||||
case .onBlack:
|
||||
ƒ = String.onBlack
|
||||
case .onBlue:
|
||||
ƒ = String.onBlue
|
||||
case .onCyan:
|
||||
ƒ = String.onCyan
|
||||
case .onDarkGray:
|
||||
ƒ = String.onDarkGray
|
||||
case .onGreen:
|
||||
ƒ = String.onGreen
|
||||
case .onLightBlue:
|
||||
ƒ = String.onLightBlue
|
||||
case .onLightCyan:
|
||||
ƒ = String.onLightCyan
|
||||
case .onLightGray:
|
||||
ƒ = String.onLightGray
|
||||
case .onLightGreen:
|
||||
ƒ = String.onLightGreen
|
||||
case .onLightMagenta:
|
||||
ƒ = String.onLightMagenta
|
||||
case .onLightRed:
|
||||
ƒ = String.onLightRed
|
||||
case .onLightYellow:
|
||||
ƒ = String.onLightYellow
|
||||
case .onMagenta:
|
||||
ƒ = String.onMagenta
|
||||
case .onRed:
|
||||
ƒ = String.onRed
|
||||
case .onWhite:
|
||||
ƒ = String.onWhite
|
||||
case .onYellow:
|
||||
ƒ = String.onYellow
|
||||
case .red:
|
||||
ƒ = String.red
|
||||
case .white:
|
||||
ƒ = String.white
|
||||
case .yellow:
|
||||
ƒ = String.yellow
|
||||
|
||||
case .bold:
|
||||
ƒ = String.bold
|
||||
case .dim:
|
||||
ƒ = String.dim
|
||||
case .italic:
|
||||
ƒ = String.italic
|
||||
case .underline:
|
||||
ƒ = String.underline
|
||||
case .blink:
|
||||
ƒ = String.blink
|
||||
case .reverse:
|
||||
ƒ = String.reverse
|
||||
case .hidden:
|
||||
ƒ = String.hidden
|
||||
case .strikethrough:
|
||||
ƒ = String.strikethrough
|
||||
case .reset:
|
||||
ƒ = String.reset
|
||||
}
|
||||
|
||||
return flip(ƒ)()
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
extension Logger.Metadata {
|
||||
|
||||
public static let tuist: String = "is"
|
||||
|
||||
public static let successKey: String = "success"
|
||||
public static var success: Logger.Metadata {
|
||||
return [ tuist: .string(successKey) ]
|
||||
}
|
||||
|
||||
public static let sectionKey: String = "section"
|
||||
public static var section: Logger.Metadata {
|
||||
return [ tuist: .string(sectionKey) ]
|
||||
}
|
||||
|
||||
public static let subsectionKey: String = "subsection"
|
||||
public static var subsection: Logger.Metadata {
|
||||
return [ tuist: .string(subsectionKey) ]
|
||||
}
|
||||
|
||||
public static let prettyKey: String = "pretty"
|
||||
public static var pretty: Logger.Metadata {
|
||||
return [ tuist: .string(prettyKey) ]
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,9 @@ let logger = Logger(label: "io.tuist.support")
|
|||
public struct LoggingConfig {
|
||||
|
||||
public enum LoggerType {
|
||||
case console, detailed, osLog
|
||||
case console
|
||||
case detailed
|
||||
case osLog
|
||||
}
|
||||
|
||||
public var loggerType: LoggerType
|
||||
|
@ -15,6 +17,7 @@ public struct LoggingConfig {
|
|||
}
|
||||
|
||||
extension LoggingConfig {
|
||||
|
||||
public static var `default`: LoggingConfig {
|
||||
|
||||
let environment = ProcessInfo.processInfo.environment
|
||||
|
@ -31,8 +34,8 @@ extension LoggingConfig {
|
|||
return .init(loggerType: .console, verbose: verbose)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum LogOutput {
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
extension Logger.Message {
|
||||
|
||||
func colorize(for logLevel: Logger.Level) -> Logger.Message {
|
||||
Logger.Message(stringLiteral: token(for: logLevel)?.apply(to: description) ?? description)
|
||||
}
|
||||
|
||||
func token(for logLevel: Logger.Level) -> Set<ConsoleToken>? {
|
||||
switch logLevel {
|
||||
case .critical:
|
||||
return [ .red, .bold ]
|
||||
case .error:
|
||||
return [ .red ]
|
||||
case .warning:
|
||||
return [ .yellow ]
|
||||
case .notice:
|
||||
return [ .bold ]
|
||||
case .debug, .trace, .info:
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -22,28 +22,36 @@ public struct StandardLogHandler: LogHandler {
|
|||
metadata: Logger.Metadata?,
|
||||
file: String, function: String, line: UInt
|
||||
) {
|
||||
|
||||
let log: Logger.Message
|
||||
|
||||
if Environment.shared.shouldOutputBeColoured {
|
||||
|
||||
switch metadata?[Logger.Metadata.colored] {
|
||||
case .string(Logger.Metadata.successKey)?:
|
||||
log = Logger.Message(stringLiteral: message.description.apply([ .green, .bold ]))
|
||||
case .string(Logger.Metadata.sectionKey)?:
|
||||
log = Logger.Message(stringLiteral: message.description.apply([ .cyan, .bold ]))
|
||||
case .string(Logger.Metadata.subsectionKey)?:
|
||||
log = Logger.Message(stringLiteral: message.description.apply([ .cyan ]))
|
||||
default:
|
||||
log = message.colorize(for: level)
|
||||
if metadata?.keys.contains(Logger.Metadata.prettyKey) == true {
|
||||
return
|
||||
}
|
||||
|
||||
let string: String
|
||||
|
||||
switch metadata?[Logger.Metadata.tuist] {
|
||||
case Logger.Metadata.successKey?:
|
||||
string = message.description.green().bold()
|
||||
case Logger.Metadata.sectionKey?:
|
||||
string = message.description.cyan().bold()
|
||||
case Logger.Metadata.subsectionKey?:
|
||||
string = message.description.cyan()
|
||||
default:
|
||||
|
||||
switch level {
|
||||
case .critical:
|
||||
string = message.description.red().bold()
|
||||
case .error:
|
||||
string = message.description.red()
|
||||
case .warning:
|
||||
string = message.description.yellow()
|
||||
case .notice, .info, .debug, .trace:
|
||||
string = message.description
|
||||
}
|
||||
|
||||
} else {
|
||||
log = message
|
||||
}
|
||||
|
||||
output(for: level).print(log.description)
|
||||
|
||||
|
||||
output(for: level).print(string)
|
||||
}
|
||||
|
||||
func output(for level: Logger.Level) -> FileHandle {
|
||||
|
@ -69,3 +77,12 @@ extension FileHandle {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func ~= (lhs: String, rhs: Logger.MetadataValue) -> Bool {
|
||||
|
||||
switch rhs {
|
||||
case let .string(s): return lhs == s
|
||||
default: return false
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -164,22 +164,28 @@ public class FileHandler: FileHandling {
|
|||
}
|
||||
|
||||
public func exists(_ path: AbsolutePath) -> Bool {
|
||||
fileManager.fileExists(atPath: path.pathString)
|
||||
let exists = fileManager.fileExists(atPath: path.pathString)
|
||||
logger.debug("Checking if \(path) exists... \(exists)")
|
||||
return exists
|
||||
}
|
||||
|
||||
public func copy(from: AbsolutePath, to: AbsolutePath) throws {
|
||||
logger.debug("Copying file from \(from) to \(to)")
|
||||
try fileManager.copyItem(atPath: from.pathString, toPath: to.pathString)
|
||||
}
|
||||
|
||||
public func move(from: AbsolutePath, to: AbsolutePath) throws {
|
||||
logger.debug("Moving file from \(from) to \(to)")
|
||||
try fileManager.moveItem(atPath: from.pathString, toPath: to.pathString)
|
||||
}
|
||||
|
||||
public func readFile(_ at: AbsolutePath) throws -> Data {
|
||||
try Data(contentsOf: at.url)
|
||||
logger.debug("Reading contents of file at path \(at)")
|
||||
return try Data(contentsOf: at.url)
|
||||
}
|
||||
|
||||
public func readTextFile(_ at: AbsolutePath) throws -> String {
|
||||
logger.debug("Reading contents of text file at path \(at)")
|
||||
let data = try Data(contentsOf: at.url)
|
||||
if let content = String(data: data, encoding: .utf8) {
|
||||
return content
|
||||
|
@ -189,6 +195,7 @@ public class FileHandler: FileHandling {
|
|||
}
|
||||
|
||||
public func readPlistFile<T: Decodable>(_ at: AbsolutePath) throws -> T {
|
||||
logger.debug("Reading contents of plist file at path \(at)")
|
||||
guard let data = fileManager.contents(atPath: at.pathString) else {
|
||||
throw FileHandlerError.fileNotFound(at)
|
||||
}
|
||||
|
@ -196,16 +203,19 @@ public class FileHandler: FileHandling {
|
|||
}
|
||||
|
||||
public func linkFile(atPath: AbsolutePath, toPath: AbsolutePath) throws {
|
||||
logger.debug("Creating a link from \(atPath) to \(toPath)")
|
||||
try fileManager.linkItem(atPath: atPath.pathString, toPath: toPath.pathString)
|
||||
}
|
||||
|
||||
public func write(_ content: String, path: AbsolutePath, atomically: Bool) throws {
|
||||
logger.debug("Writing contents to file \(path) atomically \(atomically)")
|
||||
do {
|
||||
try content.write(to: path.url, atomically: atomically, encoding: .utf8)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
public func locateDirectory(_ path: String, traversingFrom from: AbsolutePath) -> AbsolutePath? {
|
||||
logger.debug("Traversing \(from) to locate \(path)")
|
||||
let extendedPath = from.appending(RelativePath(path))
|
||||
if exists(extendedPath) {
|
||||
return extendedPath
|
||||
|
@ -221,16 +231,19 @@ public class FileHandler: FileHandling {
|
|||
}
|
||||
|
||||
public func createFolder(_ path: AbsolutePath) throws {
|
||||
logger.debug("Creating folder at path \(path)")
|
||||
try fileManager.createDirectory(at: path.url,
|
||||
withIntermediateDirectories: true,
|
||||
attributes: nil)
|
||||
}
|
||||
|
||||
public func delete(_ path: AbsolutePath) throws {
|
||||
logger.debug("Deleting item at path \(path)")
|
||||
try fileManager.removeItem(atPath: path.pathString)
|
||||
}
|
||||
|
||||
public func touch(_ path: AbsolutePath) throws {
|
||||
logger.debug("Touching \(path)")
|
||||
try fileManager.createDirectory(at: path.removingLastComponent().url,
|
||||
withIntermediateDirectories: true,
|
||||
attributes: nil)
|
||||
|
@ -244,6 +257,9 @@ public class FileHandler: FileHandling {
|
|||
}
|
||||
|
||||
public func locateDirectoryTraversingParents(from: AbsolutePath, path: String) -> AbsolutePath? {
|
||||
|
||||
logger.debug("Traversing \(from) to locate \(path)")
|
||||
|
||||
let tuistConfigPath = from.appending(component: path)
|
||||
|
||||
if FileHandler.shared.exists(tuistConfigPath) {
|
||||
|
|
|
@ -1,188 +1,158 @@
|
|||
//import Basic
|
||||
//import Foundation
|
||||
//
|
||||
//public struct PrintableString: Encodable, Decodable, Equatable {
|
||||
// /// Contains a string that can be interpolated with options.
|
||||
// let rawString: String
|
||||
//}
|
||||
//
|
||||
//extension PrintableString: ExpressibleByStringLiteral {
|
||||
// public init(stringLiteral: String) {
|
||||
// rawString = stringLiteral
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//extension PrintableString: CustomStringConvertible {
|
||||
// public var description: String {
|
||||
// rawString
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//extension PrintableString: ExpressibleByStringInterpolation {
|
||||
// public init(stringInterpolation: StringInterpolation) {
|
||||
// rawString = stringInterpolation.string
|
||||
// }
|
||||
//
|
||||
// public struct StringInterpolation: StringInterpolationProtocol {
|
||||
// var string: String
|
||||
//
|
||||
// public init(literalCapacity _: Int, interpolationCount _: Int) {
|
||||
// string = String()
|
||||
// }
|
||||
//
|
||||
// public mutating func appendLiteral(_ literal: String) {
|
||||
// string.append(literal)
|
||||
// }
|
||||
//
|
||||
// public mutating func appendInterpolation(_ token: PrintableString.Token) {
|
||||
// string.append(token.description)
|
||||
// }
|
||||
//
|
||||
// public mutating func appendInterpolation(_ value: String) {
|
||||
// string.append(value)
|
||||
// }
|
||||
//
|
||||
// public mutating func appendInterpolation(_ value: CustomStringConvertible) {
|
||||
// string.append(value.description)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//extension PrintableString {
|
||||
// public indirect enum Token: ExpressibleByStringLiteral {
|
||||
// case raw(String)
|
||||
// case command(Token)
|
||||
// case keystroke(Token)
|
||||
// case bold(Token)
|
||||
// case error(Token)
|
||||
// case success(Token)
|
||||
// case warning(Token)
|
||||
// case info(Token)
|
||||
//
|
||||
// public init(stringLiteral: String) {
|
||||
// self = .raw(stringLiteral)
|
||||
// }
|
||||
//
|
||||
// public var description: String {
|
||||
// switch self {
|
||||
// case let .raw(string):
|
||||
// return string
|
||||
// case let .command(token):
|
||||
// return Environment.shared.shouldOutputBeColoured ? token.description.cyan() : token.description
|
||||
// case let .keystroke(token):
|
||||
// return Environment.shared.shouldOutputBeColoured ? token.description.cyan() : token.description
|
||||
// case let .bold(token):
|
||||
// return Environment.shared.shouldOutputBeColoured ? token.description.bold() : token.description
|
||||
// case let .error(token):
|
||||
// return Environment.shared.shouldOutputBeColoured ? token.description.red() : token.description
|
||||
// case let .success(token):
|
||||
// return Environment.shared.shouldOutputBeColoured ? token.description.green() : token.description
|
||||
// case let .warning(token):
|
||||
// return Environment.shared.shouldOutputBeColoured ? token.description.yellow() : token.description
|
||||
// case let .info(token):
|
||||
// return Environment.shared.shouldOutputBeColoured ? token.description.lightBlue() : token.description
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//public protocol Printing: AnyObject {
|
||||
// func print(_ text: PrintableString)
|
||||
// func print(section: PrintableString)
|
||||
// func print(subsection: PrintableString)
|
||||
// func print(warning: PrintableString)
|
||||
// func print(error: Error)
|
||||
// func print(success: PrintableString)
|
||||
// func print(errorMessage: PrintableString)
|
||||
// func print(deprecation: PrintableString)
|
||||
//}
|
||||
//
|
||||
//public class Printer: Printing {
|
||||
// /// Shared instance
|
||||
// public static var shared: Printing = Printer()
|
||||
//
|
||||
// // MARK: - Init
|
||||
//
|
||||
// init() {}
|
||||
//
|
||||
// // MARK: - Public
|
||||
//
|
||||
// public func print(_ text: PrintableString) {
|
||||
// printStandardOutputLine(text.description)
|
||||
// }
|
||||
//
|
||||
// public func print(error: Error) {
|
||||
// if Environment.shared.shouldOutputBeColoured {
|
||||
// printStandardErrorLine("Error: \(error.localizedDescription)".localizedDescription.red().bold())
|
||||
// } else {
|
||||
// printStandardErrorLine("Error: \(error.localizedDescription)")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public func print(success: PrintableString) {
|
||||
// if Environment.shared.shouldOutputBeColoured {
|
||||
// printStandardOutputLine("Success: \(success)".green().bold())
|
||||
// } else {
|
||||
// printStandardOutputLine("Success: \(success)")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Prints a deprecation message (yellow color)
|
||||
// ///
|
||||
// /// - Parameter deprecation: Deprecation message.
|
||||
// public func print(deprecation: PrintableString) {
|
||||
// if Environment.shared.shouldOutputBeColoured {
|
||||
// printStandardOutputLine("Deprecated: \(deprecation)".yellow().bold())
|
||||
// } else {
|
||||
// printStandardOutputLine("Deprecated: \(deprecation)")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public func print(warning: PrintableString) {
|
||||
// if Environment.shared.shouldOutputBeColoured {
|
||||
// printStandardOutputLine("Warning: \(warning)".yellow().bold())
|
||||
// } else {
|
||||
// printStandardOutputLine("Warning: \(warning)")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public func print(errorMessage: PrintableString) {
|
||||
// if Environment.shared.shouldOutputBeColoured {
|
||||
// printStandardErrorLine("Error: \(errorMessage)".red().bold())
|
||||
// } else {
|
||||
// printStandardErrorLine("Error: \(errorMessage)")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public func print(section: PrintableString) {
|
||||
// if Environment.shared.shouldOutputBeColoured {
|
||||
// printStandardOutputLine(section.description.cyan().bold())
|
||||
// } else {
|
||||
// printStandardOutputLine(section.description)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public func print(subsection: PrintableString) {
|
||||
// if Environment.shared.shouldOutputBeColoured {
|
||||
// printStandardOutputLine(subsection.description.cyan())
|
||||
// } else {
|
||||
// printStandardOutputLine(subsection.description)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // MARK: - Fileprivate
|
||||
//
|
||||
// fileprivate func printStandardOutputLine(_ string: String) {
|
||||
// if let data = string.data(using: .utf8) {
|
||||
// FileHandle.standardOutput.write(data)
|
||||
// }
|
||||
// FileHandle.standardOutput.write("\n".data(using: .utf8)!)
|
||||
// }
|
||||
//
|
||||
// fileprivate func printStandardErrorLine(_ string: String) {
|
||||
// if let data = string.data(using: .utf8) {
|
||||
// FileHandle.standardError.write(data)
|
||||
// }
|
||||
// FileHandle.standardError.write("\n".data(using: .utf8)!)
|
||||
// }
|
||||
//}
|
||||
import Foundation
|
||||
|
||||
public struct PrintableString: Encodable, Decodable, Equatable {
|
||||
/// Contains a string that can be interpolated with options.
|
||||
let rawString: String
|
||||
let pretty: String
|
||||
}
|
||||
|
||||
extension PrintableString: ExpressibleByStringLiteral {
|
||||
public init(stringLiteral: String) {
|
||||
rawString = stringLiteral
|
||||
pretty = stringLiteral
|
||||
}
|
||||
}
|
||||
|
||||
extension PrintableString: CustomStringConvertible, CustomDebugStringConvertible {
|
||||
public var description: String {
|
||||
pretty
|
||||
}
|
||||
|
||||
public var debugDescription: String {
|
||||
rawString
|
||||
}
|
||||
}
|
||||
|
||||
extension PrintableString: ExpressibleByStringInterpolation {
|
||||
public init(stringInterpolation: StringInterpolation) {
|
||||
rawString = stringInterpolation.unformatted
|
||||
pretty = stringInterpolation.string
|
||||
}
|
||||
|
||||
public struct StringInterpolation: StringInterpolationProtocol {
|
||||
var unformatted: String
|
||||
var string: String
|
||||
|
||||
public init(literalCapacity _: Int, interpolationCount _: Int) {
|
||||
string = ""
|
||||
unformatted = ""
|
||||
}
|
||||
|
||||
public mutating func appendLiteral(_ literal: String) {
|
||||
string.append(literal)
|
||||
unformatted.append(literal)
|
||||
}
|
||||
|
||||
public mutating func appendInterpolation(_ token: PrintableString.Token) {
|
||||
string.append(token.description)
|
||||
unformatted.append(token.unformatted)
|
||||
}
|
||||
|
||||
public mutating func appendInterpolation(_ value: String) {
|
||||
string.append(value)
|
||||
unformatted.append(value)
|
||||
}
|
||||
|
||||
public mutating func appendInterpolation(_ value: CustomStringConvertible) {
|
||||
string.append(value.description)
|
||||
unformatted.append(value.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PrintableString {
|
||||
public indirect enum Token: ExpressibleByStringLiteral {
|
||||
case raw(String)
|
||||
case command(Token)
|
||||
case keystroke(Token)
|
||||
case bold(Token)
|
||||
case error(Token)
|
||||
case success(Token)
|
||||
case warning(Token)
|
||||
case info(Token)
|
||||
|
||||
public init(stringLiteral: String) {
|
||||
self = .raw(stringLiteral)
|
||||
}
|
||||
|
||||
public var unformatted: String {
|
||||
switch self {
|
||||
case let .raw(string):
|
||||
return string
|
||||
case let .command(token):
|
||||
return token.description
|
||||
case let .keystroke(token):
|
||||
return token.description
|
||||
case let .bold(token):
|
||||
return token.description
|
||||
case let .error(token):
|
||||
return token.description
|
||||
case let .success(token):
|
||||
return token.description
|
||||
case let .warning(token):
|
||||
return token.description
|
||||
case let .info(token):
|
||||
return token.description
|
||||
}
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case let .raw(string):
|
||||
return string
|
||||
case let .command(token):
|
||||
return Environment.shared.shouldOutputBeColoured ? token.description.cyan() : token.description
|
||||
case let .keystroke(token):
|
||||
return Environment.shared.shouldOutputBeColoured ? token.description.cyan() : token.description
|
||||
case let .bold(token):
|
||||
return Environment.shared.shouldOutputBeColoured ? token.description.bold() : token.description
|
||||
case let .error(token):
|
||||
return Environment.shared.shouldOutputBeColoured ? token.description.red() : token.description
|
||||
case let .success(token):
|
||||
return Environment.shared.shouldOutputBeColoured ? token.description.green() : token.description
|
||||
case let .warning(token):
|
||||
return Environment.shared.shouldOutputBeColoured ? token.description.yellow() : token.description
|
||||
case let .info(token):
|
||||
return Environment.shared.shouldOutputBeColoured ? token.description.lightBlue() : token.description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Logger {
|
||||
|
||||
/// Log a message passing with the `Logger.Level.notice` log level.
|
||||
///
|
||||
/// `pretty` is always printed to the console, and is omitted to the logger as `notice`.
|
||||
/// The standard swift-log API will recieve the
|
||||
///
|
||||
/// - parameters:
|
||||
/// - message: The message to be logged. `message` can be used with any string interpolation literal.
|
||||
/// - metadata: One-off metadata to attach to this log message
|
||||
/// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
|
||||
/// defaults to `#file`).
|
||||
/// - function: The function this log message originates from (there's usually no need to pass it explicitly as
|
||||
/// it defaults to `#function`).
|
||||
/// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
|
||||
/// defaults to `#line`).
|
||||
public func pretty(
|
||||
_ message: @autoclosure () -> PrintableString,
|
||||
metadata: @autoclosure () -> Logger.Metadata? = nil,
|
||||
file: String = #file, function: String = #function, line: UInt = #line
|
||||
) {
|
||||
|
||||
let printableString = message()
|
||||
|
||||
self.log(
|
||||
level: .notice, Logger.Message(stringLiteral: printableString.rawString),
|
||||
metadata: metadata()?.merging(.pretty, uniquingKeysWith: { $1 }),
|
||||
file: file,
|
||||
function: function,
|
||||
line: line
|
||||
)
|
||||
|
||||
FileHandle.standardOutput.print(printableString.pretty)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,12 +37,24 @@ The project is organized in several targets that are defined in the `Package.swi
|
|||
|
||||
All generic utilities live in the `TuistSupport` framework. This section documents some of the utilities and how to use them.
|
||||
|
||||
### Printer
|
||||
### Logger
|
||||
|
||||
When printing output for the user, this is the utility that should be used over the global `print` method. The utility provides formatting that varies depending on the terminal the process is run from, and determines whether the standard output or error should be used based on the type of content being output.
|
||||
|
||||
The methods from the public interface take a `PrintableString` as an input. Printable strings support interpolating formatted strings. For instance, if we want to tell users they need to run a command, we can do the following:
|
||||
If you want to print and do not want any special formatting you can use the normal `swift-log` API for printing. There is a variable in each tuist module called `logger` - any new modules should also adopt this style of logging to fit within the rest of the Tuist system.
|
||||
|
||||
```swift
|
||||
logger.info("Please run the folling command to setup the environment: \(.command("tuist up"))")
|
||||
logger.critical("") // Use `critical` for unrecoverable, system errors.
|
||||
logger.error("") // Use `error` for user errors, particularly problems with their machine, manifest or configuration.
|
||||
logger.warning("") // Use `warning` to highlight potential issues.
|
||||
logger.notice("") // Use `notice` to log to console, this is the normal level of logging.
|
||||
logger.info("") // Use `info` to provide small meta messages, this will be printed but won't be promenant.
|
||||
logger.debug("") // Use `debug` to print in verbose mode.
|
||||
logger.pretty("") // Use `pretty` to print a string with formatted interpolations, useful for highlighting certain elements in the string.
|
||||
```
|
||||
|
||||
The `logger.pretty` takes a `PrintableString` as an input. Printable strings support interpolating formatted strings. For instance, if we want to tell users they need to run a command, we can do the following:
|
||||
|
||||
```swift
|
||||
logger.pretty("Please run the folling command to setup the environment: \(.command("tuist up"))")
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue