using System; using System.Diagnostics; using System.Text; internal sealed class StatusReporter { private int _dotCounter = 0; private string _lastMessage = string.Empty; private DateTime _lastWriteTime = DateTime.MinValue; public void Show(string message, bool withDots = false) { if (withDots) { _dotCounter++; message = $"{message}{Dots()}"; } Write(message, newline: false); } public string Dots() { var dots = (_dotCounter % 3) + 1; return new string('.', dots); } public void ShowLine(string message) { Write(message, newline: true); } public void ShowProgress(string message, TimeSpan levelElapsed, TimeSpan totalElapsed) { _dotCounter++; var progress = $"{message}{Dots()}({FormatDuration(levelElapsed)} / {FormatDuration(totalElapsed)})"; Write(progress, newline: false); } private void Write(string message, bool newline) { if (_lastMessage == message && !newline) return; var now = DateTime.UtcNow; // 너무 자주 찍으면 콘솔이 쓸데없이 지저분해지므로, // 진행 중 상태는 최소 1초 간격으로만 업데이트한다. if (!newline && (now - _lastWriteTime).TotalMilliseconds < 1000) { return; } _lastWriteTime = now; var pad = string.Empty; _lastMessage = message; var output = $"\r\u001b[K{message}{pad}"; if (newline) { Console.Error.WriteLine(output); _lastMessage = string.Empty; } else { Console.Error.Write(output); } } public static string FormatDuration(TimeSpan elapsed) { if (elapsed.TotalHours >= 1) { return $"{(int)elapsed.TotalHours}h{elapsed.Minutes}m{elapsed.Seconds}s"; } if (elapsed.TotalMinutes >= 1) { return $"{(int)elapsed.TotalMinutes}m{elapsed.Seconds}s"; } return $"{elapsed.Seconds}s"; } }