/// <summary>Writes the specified <see cref="ConsoleColoredString"/> to the console.</summary> public static void Write(ConsoleColoredString value, bool stdErr = false) { if (value != null) { value.writeTo(stdErr ? Console.Error : Console.Out); } }
/// <summary> /// Outputs the specified coloured message, marked up using EggsML, to the console window, treating newlines as /// paragraph breaks. All paragraphs are word-wrapped to fit in the console buffer, or to a sensible width if /// redirected to a file. Each paragraph is indented by the number of spaces at the start of the corresponding /// line.</summary> /// <param name="message"> /// The message to output.</param> /// <param name="hangingIndent"> /// Specifies a number of spaces by which the message is indented in all but the first line of each paragraph.</param> /// <remarks> /// See <see cref="EggsNode.ToConsoleColoredStringWordWrap"/> for the colour syntax.</remarks> public static void WriteParagraphs(EggsNode message, int hangingIndent = 0) { int width; try { width = WrapToWidth(); } catch { // Fall back to non-word-wrapping WriteLine(ConsoleColoredString.FromEggsNode(message)); return; } bool any = false; foreach (var line in message.ToConsoleColoredStringWordWrap(width, hangingIndent)) { WriteLine(line); any = true; } // Special case: if the input is empty, output an empty line if (!any) { Console.WriteLine(); } }
/// <summary> /// Invokes the command, blocking until the command finishes. Returns the command's exit code. Does not throw if /// the command failed.</summary> public int GoGetExitCode() { var invokeCount = Interlocked.Increment(ref _invokeCount) + 1; var startTime = DateTime.UtcNow; if (_printCommandOutput && _printAugmented) { var prefix = (_printInvokeCount ? " Cmd {0} at {1:HH:mm}> " : " {1:HH:mm}> ").Color(ConsoleColor.White); ConsoleUtil.WriteLine("Running command: ".Color(ConsoleColor.Yellow) + _runner.Command.Color(ConsoleColor.Cyan)); ConsoleUtil.Write(ConsoleColoredString.Format(prefix, invokeCount, DateTime.UtcNow)); _runner.StdoutText += txt => { ConsoleUtil.Write(ConsoleColoredString.Format(txt.Color(ConsoleColor.Gray).Replace("\n", "\n" + prefix), invokeCount, DateTime.UtcNow)); }; _runner.StderrText += txt => { ConsoleUtil.Write(ConsoleColoredString.Format(txt.Color(ConsoleColor.Red).Replace("\n", "\n" + prefix), invokeCount, DateTime.UtcNow)); }; } else if (_printCommandOutput) { _runner.StdoutText += txt => { Console.Write(txt); }; _runner.StderrText += txt => { ConsoleUtil.Write(txt.Color(ConsoleColor.Red)); }; } _runner.Start(); _runner.EndedWaitHandle.WaitOne(); if (_printCommandOutput && _printAugmented) { Console.WriteLine(); var ranFor = "Ran for {0:#,0} seconds".Fmt((DateTime.UtcNow - startTime).TotalSeconds); if (isSuccess(_runner.ExitCode)) { ConsoleUtil.WriteLine("Command succeeded. {0}\r\n\r\n".Fmt(ranFor).Color(ConsoleColor.Green)); } else { ConsoleUtil.WriteLine("Command failed with error code {0}. {1}\r\n\r\n".Fmt(_runner.ExitCode, ranFor).Color(ConsoleColor.Red)); } } return(_runner.ExitCode); }
private static void tellUser(ConsoleColoredString message) { #if CONSOLE ConsoleUtil.WriteLine(message); #else new RT.Util.Dialogs.DlgMessage() { Message = message.ToString(), Type = RT.Util.Dialogs.DlgType.Warning, Font = new System.Drawing.Font("Consolas", 9) }.Show(); #endif }
/// <summary> /// Word-wraps the current <see cref="ConsoleColoredString"/> to a specified width. Supports UNIX-style newlines /// and indented paragraphs.</summary> /// <remarks> /// <para> /// The supplied text will be split into "paragraphs" at the newline characters. Every paragraph will begin on /// a new line in the word-wrapped output, indented by the same number of spaces as in the input. All /// subsequent lines belonging to that paragraph will also be indented by the same amount.</para> /// <para> /// All multiple contiguous spaces will be replaced with a single space (except for the indentation).</para></remarks> /// <param name="text"> /// Text to be word-wrapped.</param> /// <param name="maxWidth"> /// The maximum number of characters permitted on a single line, not counting the end-of-line terminator.</param> /// <param name="hangingIndent"> /// The number of spaces to add to each line except the first of each paragraph, thus creating a hanging /// indentation.</param> public static IEnumerable <ConsoleColoredString> WordWrap(this ConsoleColoredString text, int maxWidth, int hangingIndent = 0) { if (text == null) { throw new ArgumentNullException(nameof(text)); } if (maxWidth < 1) { throw new ArgumentOutOfRangeException(nameof(maxWidth), maxWidth, "maxWidth cannot be less than 1"); } if (hangingIndent < 0) { throw new ArgumentOutOfRangeException(nameof(hangingIndent), hangingIndent, "hangingIndent cannot be negative."); } if (text == null || text.Length == 0) { return(Enumerable.Empty <ConsoleColoredString>()); } return(StringExtensions.wordWrap( text.Split(new string[] { "\r\n", "\r", "\n" }), maxWidth, hangingIndent, (txt, substrIndex) => txt.Substring(substrIndex).Split(new string[] { " " }, options: StringSplitOptions.RemoveEmptyEntries), cc => cc.Length, txt => { // Count the number of spaces at the start of the paragraph int indentLen = 0; while (indentLen < txt.Length && txt.CharAt(indentLen) == ' ') { indentLen++; } return indentLen; }, num => new string(' ', num), () => new List <ConsoleColoredString>(), list => list.Sum(c => c.Length), (list, cc) => { list.Add(cc); }, list => new ConsoleColoredString(list), (str, start, length) => length == null ? str.Substring(start) : str.Substring(start, length.Value), (str1, str2) => str1 + str2)); }
static int Main(string[] args) { if (args.Length == 2 && args[0] == "--post-build-check") { return(Ut.RunPostBuildChecks(args[1], Assembly.GetExecutingAssembly())); } var settingsFile = PathUtil.AppPathCombine("MySrvMon.xml"); if (!File.Exists(settingsFile)) { var sample = new Settings(); sample.Modules.Add(new SmartModule()); ClassifyXml.SerializeToFile(sample, settingsFile + ".sample"); Console.WriteLine("Sample settings file saved: " + settingsFile + ".sample"); Console.WriteLine("Edit and rename to " + settingsFile); return(1); } var settings = ClassifyXml.DeserializeFile <Settings>(settingsFile); ClassifyXml.SerializeToFile(settings, settingsFile + ".rewrite"); // for SMTP password encryption foreach (var module in settings.Modules) { module.Execute(); } foreach (var module in settings.Modules.OrderByDescending(m => m.Status)) { ConsoleColoredString report = "===========================\r\n" + "=== " + module.Name + "\r\n" + "===========================\r\n\r\n"; report = report.Color(module.Status.GetConsoleColor()); report += module.ConsoleReport + "\r\n"; ConsoleUtil.Write(report); } var worstStatus = settings.Modules.Max(v => v.Status); return((int)worstStatus); }
/// <summary> /// Writes the specified <see cref="ConsoleColoredString"/> followed by a newline to the console.</summary> /// <param name="value"> /// The string to print to the console.</param> /// <param name="stdErr"> /// <c>true</c> to print to Standard Error instead of Standard Output.</param> /// <param name="align"> /// Horizontal alignment of the string within the remaining space of the current line. If the string does not fit, /// it will be printed as if left-aligned.</param> public static void WriteLine(ConsoleColoredString value, bool stdErr = false, HorizontalTextAlignment align = HorizontalTextAlignment.Left) { var output = stdErr ? Console.Error : Console.Out; if (value != null) { var cursorLeft = 0; try { cursorLeft = Console.CursorLeft; } catch { } var width = WrapToWidth() - cursorLeft; if (align == HorizontalTextAlignment.Center && width > value.Length) { output.Write(new string(' ', (width - value.Length) / 2)); } else if (align == HorizontalTextAlignment.Right && width > value.Length) { output.Write(new string(' ', width - value.Length)); } value.writeTo(output); } output.WriteLine(); }
/// <summary> /// Outputs the specified message to the console window, treating newlines as paragraph breaks. All paragraphs are /// word-wrapped to fit in the console buffer, or to a sensible width if redirected to a file. Each paragraph is /// indented by the number of spaces at the start of the corresponding line.</summary> /// <param name="message"> /// The message to output.</param> /// <param name="hangingIndent"> /// Specifies a number of spaces by which the message is indented in all but the first line of each paragraph.</param> public static void WriteParagraphs(ConsoleColoredString message, int hangingIndent = 0) { if (message == null) { throw new ArgumentNullException(nameof(message)); } int width; try { width = WrapToWidth(); } catch { ConsoleUtil.WriteLine(message); return; } foreach (var line in message.WordWrap(width, hangingIndent)) { ConsoleUtil.WriteLine(line); } }
private void toString(int?maxWidth, Action <string> outputString, Action <ConsoleColoredString> outputColoredString) { int rows = _cells.Count; if (rows == 0) { return; } int cols = _cells.Max(row => row.Count); // Create a lookup array which, for each column, and for each possible value of colspan, tells you which cells in that column have this colspan and end in this column var cellsByColspan = new SortedDictionary <int, List <int> > [cols]; for (var col = 0; col < cols; col++) { var cellsInThisColumn = new SortedDictionary <int, List <int> >(); for (int row = 0; row < rows; row++) { if (col >= _cells[row].Count) { continue; } var cel = _cells[row][col]; if (cel == null) { continue; } if (cel is surrogateCell && ((surrogateCell)cel).RealRow != row) { continue; } int realCol = cel is surrogateCell ? ((surrogateCell)cel).RealCol : col; var realCell = (trueCell)_cells[row][realCol]; if (realCol + realCell.ColSpan - 1 != col) { continue; } cellsInThisColumn.AddSafe(realCell.ColSpan, row); } cellsByColspan[col] = cellsInThisColumn; } // Find out the width that each column would have if the text wasn't wrapped. // If this fits into the total width, then we want each column to be at least this wide. var columnWidths = generateColumnWidths(cols, cellsByColspan, c => Math.Max(1, c.LongestParagraph())); var unwrapped = true; // If the table is now too wide, use the length of the longest word, or longest paragraph if nowrap if (maxWidth != null && columnWidths.Sum() > maxWidth - (cols - 1) * ColumnSpacing) { columnWidths = generateColumnWidths(cols, cellsByColspan, c => Math.Max(1, c.MinWidth())); unwrapped = false; } // If the table is still too wide, use the length of the longest paragraph if nowrap, otherwise 0 if (maxWidth != null && columnWidths.Sum() > maxWidth - (cols - 1) * ColumnSpacing) { columnWidths = generateColumnWidths(cols, cellsByColspan, c => c.NoWrap ? Math.Max(1, c.LongestParagraph()) : 1); } // If the table is still too wide, we will have to wrap like crazy. if (maxWidth != null && columnWidths.Sum() > maxWidth - (cols - 1) * ColumnSpacing) { columnWidths = new int[cols]; for (int i = 0; i < cols; i++) { columnWidths[i] = 1; } } // If the table is STILL too wide, all bets are off. if (maxWidth != null && columnWidths.Sum() > maxWidth - (cols - 1) * ColumnSpacing) { throw new InvalidOperationException(@"The specified table width is too narrow. It is not possible to fit the {0} columns and the column spacing of {1} per column into a total width of {2} characters.".Fmt(cols, ColumnSpacing, maxWidth)); } // If we have any extra width to spare... var missingTotalWidth = maxWidth == null ? 0 : maxWidth - columnWidths.Sum() - (cols - 1) * ColumnSpacing; if (missingTotalWidth > 0 && (UseFullWidth || !unwrapped)) { // Use the length of the longest paragraph in each column to calculate a proportion by which to enlarge each column var widthProportionByCol = new int[cols]; for (var col = 0; col < cols; col++) { foreach (var kvp in cellsByColspan[col]) { distributeEvenly( widthProportionByCol, col, kvp.Key, kvp.Value.Max(row => ((trueCell)_cells[row][col - kvp.Key + 1]).LongestParagraph()) - widthProportionByCol.Skip(col - kvp.Key + 1).Take(kvp.Key).Sum() - (unwrapped ? 0 : columnWidths.Skip(col - kvp.Key + 1).Take(kvp.Key).Sum()) ); } } var widthProportionTotal = widthProportionByCol.Sum(); // Adjust the width of the columns according to the calculated proportions so that they fill the missing width. // We do this in two steps. Step one: enlarge the column widths by the integer part of the calculated portion (round down). // After this the width remaining will be smaller than the number of columns, so each column is missing at most 1 character. var widthRemaining = missingTotalWidth; var fractionalParts = new double[cols]; for (int col = 0; col < cols; col++) { var widthToAdd = (double)(widthProportionByCol[col] * missingTotalWidth) / widthProportionTotal; var integerPart = (int)widthToAdd; columnWidths[col] += integerPart; fractionalParts[col] = widthToAdd - integerPart; widthRemaining -= integerPart; } // Step two: enlarge a few more columns by 1 character so that we reach the desired width. // The columns with the largest fractional parts here are the furthest from what we ideally want, so we favour those. foreach (var elem in fractionalParts.Select((frac, col) => new { Value = frac, Col = col }).OrderByDescending(e => e.Value)) { if (widthRemaining < 1) { break; } columnWidths[elem.Col]++; widthRemaining--; } } // Word-wrap all the contents of all the cells trueCell truCel; foreach (var row in _cells) { for (int col = 0; col < row.Count; col++) { if ((truCel = row[col] as trueCell) != null) { truCel.Wordwrap(columnWidths.Skip(col).Take(truCel.ColSpan).Sum() + (truCel.ColSpan - 1) * ColumnSpacing); } } } // Calculate the string index for each column var strIndexByCol = new int[cols + 1]; for (var i = 0; i < cols; i++) { strIndexByCol[i + 1] = strIndexByCol[i] + columnWidths[i] + ColumnSpacing; } var realWidth = strIndexByCol[cols] - ColumnSpacing; // Make sure we don't render rules if we can't bool verticalRules = VerticalRules && ColumnSpacing > 0; bool horizontalRules = HorizontalRules && RowSpacing > 0; // If we do render vertical rules, where should it be (at which string offset, counted backwards from the end of the column spacing) var vertRuleOffset = (ColumnSpacing + 1) / 2; // Finally, render the entire output List <ConsoleColoredString> currentLine = null; for (int row = 0; row < rows; row++) { var rowList = _cells[row]; var extraRows = RowSpacing + 1; var isFirstIteration = true; bool anyMoreContentInThisRow; do { ConsoleColoredString previousLine = currentLine == null ? null : new ConsoleColoredString(currentLine.ToArray()); currentLine = new List <ConsoleColoredString>(); anyMoreContentInThisRow = false; for (int col = 0; col < cols; col++) { var cel = col < rowList.Count ? rowList[col] : null; // For cells with colspan, consider only the first cell they're spanning and skip the rest if (cel is surrogateCell && ((surrogateCell)cel).RealCol != col) { continue; } // If the cell has rowspan, what row did this cell start in? var valueRow = cel is surrogateCell ? ((surrogateCell)cel).RealRow : row; // Retrieve the data for the cell var realCell = col < _cells[valueRow].Count ? (trueCell)_cells[valueRow][col] : null; var colspan = realCell == null ? 1 : realCell.ColSpan; var rowspan = realCell == null ? 1 : realCell.RowSpan; var rowBackground = row >= _rowBackgrounds.Count ? null : _rowBackgrounds[row]; // Does this cell end in this row? var isLastRow = valueRow + rowspan - 1 == row; // If we are inside the cell, render one line of the contents of the cell if (realCell != null && realCell.WordwrappedValue.Length > realCell.WordwrappedIndex) { var align = realCell.Alignment ?? DefaultAlignment; var curLineLength = currentLine.Sum(c => c.Length); var cellBackground = realCell.Background ?? rowBackground; if (strIndexByCol[col] > curLineLength) { currentLine.Add(new string(' ', strIndexByCol[col] - curLineLength).Color(null, cellBackground)); } object textRaw = realCell.WordwrappedValue[realCell.WordwrappedIndex]; ConsoleColoredString text = textRaw is ConsoleColoredString ? (ConsoleColoredString)textRaw : (string)textRaw; // implicit conversion to ConsoleColoredString if (align == HorizontalTextAlignment.Center) { currentLine.Add(new string(' ', (strIndexByCol[col + colspan] - strIndexByCol[col] - ColumnSpacing - text.Length) / 2).Color(null, cellBackground)); } else if (align == HorizontalTextAlignment.Right) { currentLine.Add(new string(' ', strIndexByCol[col + colspan] - strIndexByCol[col] - ColumnSpacing - text.Length).Color(null, cellBackground)); } if (cellBackground == null) { currentLine.Add(text); } else { currentLine.Add(text.ColorBackgroundWhereNull(cellBackground.Value)); if (align == HorizontalTextAlignment.Center) { currentLine.Add(new string(' ', (strIndexByCol[col + colspan] - strIndexByCol[col] - ColumnSpacing - text.Length + 1) / 2).Color(null, cellBackground)); } else if (align == HorizontalTextAlignment.Left) { currentLine.Add(new string(' ', strIndexByCol[col + colspan] - strIndexByCol[col] - ColumnSpacing - text.Length).Color(null, cellBackground)); } } realCell.WordwrappedIndex++; } // If we are at the end of a row, render horizontal rules var horizRuleStart = col > 0 ? strIndexByCol[col] - vertRuleOffset + 1 : 0; var horizRuleEnd = (col + colspan < cols) ? strIndexByCol[col + colspan] - vertRuleOffset + (verticalRules ? 0 : 1) : realWidth; var renderingHorizontalRules = horizontalRules && isLastRow && extraRows == 1; if (renderingHorizontalRules) { currentLine.Add(new string(' ', horizRuleStart - currentLine.Sum(c => c.Length))); currentLine.Add(new string((row == HeaderRows - 1) ? '=' : '-', horizRuleEnd - horizRuleStart)); } else { var subtract = (col + colspan == cols ? ColumnSpacing : vertRuleOffset) + currentLine.Sum(c => c.Length); currentLine.Add(new string(' ', strIndexByCol[col + colspan] - subtract).Color(null, (realCell == null ? null : realCell.Background) ?? rowBackground)); } // If we are at the beginning of a row, render the horizontal rules for the row above by modifying the previous line. // We want to do this because it may have an unwanted vertical rule if this is a cell with colspan and there are // multiple cells with smaller colspans above it. if (isFirstIteration && horizontalRules && row > 0 && cel is trueCell) { previousLine = new ConsoleColoredString(previousLine.Substring(0, horizRuleStart), new string((row == HeaderRows) ? '=' : '-', horizRuleEnd - horizRuleStart), previousLine.Substring(horizRuleEnd)); } // Render vertical rules if (verticalRules && (col + colspan < cols)) { currentLine.Add((new string(' ', strIndexByCol[col + colspan] - vertRuleOffset - currentLine.Sum(c => c.Length)) + "|").Color(null, renderingHorizontalRules ? null : rowBackground)); } // Does this cell still contain any more content that needs to be output before this row can be finished? anyMoreContentInThisRow = anyMoreContentInThisRow || (realCell != null && isLastRow && realCell.WordwrappedValue.Length > realCell.WordwrappedIndex); } if (previousLine != null) { if (LeftMargin > 0) { outputString(new string(' ', LeftMargin)); } outputColoredString(previousLine); outputString(Environment.NewLine); } isFirstIteration = false; // If none of the cells in this row contain any more content, start counting down the row spacing if (!anyMoreContentInThisRow) { extraRows--; } }while (anyMoreContentInThisRow || (extraRows > 0 && row < rows - 1)); } // Output the last line if (LeftMargin > 0) { outputString(new string(' ', LeftMargin)); } outputColoredString(new ConsoleColoredString(currentLine.ToArray())); outputString(Environment.NewLine); }
/// <summary> /// Places the specified content into the cell at the specified co-ordinates.</summary> /// <param name="col"> /// Column where to place the content.</param> /// <param name="row"> /// Row where to place the content.</param> /// <param name="content"> /// The content to place.</param> /// <param name="colSpan"> /// The number of columns to span.</param> /// <param name="rowSpan"> /// The number of rows to span.</param> /// <param name="noWrap"> /// If true, indicates that this cell should not be automatically word-wrapped except at explicit newlines in /// <paramref name="content"/>. The cell is word-wrapped only if doing so is necessary to fit all no-wrap cells /// into the table's total width. If false, the cell is automatically word-wrapped to optimise the table's layout.</param> /// <param name="alignment"> /// How to align the contents within the cell, or null to use <see cref="DefaultAlignment"/>.</param> /// <param name="background"> /// Specifies a background color for the whole cell, including its empty space. Characters with background colors /// in the input string take precedence for those characters only.</param> public void SetCell(int col, int row, ConsoleColoredString content, int colSpan = 1, int rowSpan = 1, bool noWrap = false, HorizontalTextAlignment?alignment = null, ConsoleColor?background = null) { setCell(col, row, content, colSpan, rowSpan, noWrap, alignment, background); }
static void Main() { Console.OutputEncoding = Encoding.UTF8; Console.WindowWidth = 200; Console.WindowHeight = 40; var curUser = Environment.UserName; curUser = curUser.Substring(0, 1).ToUpper() + curUser.Substring(1).ToLower(); Settings settings; SettingsUtil.LoadSettings(out settings); var highscore = settings.Highscores[curUser]; var recent = new AutoDictionary <string, Highscore>(_ => new Highscore()); int cur = 0; ConsoleColoredString lastAttemptOutcome = null; while (true) { Console.Clear(); Console.WriteLine("Current highscores, in milliseconds:"); WriteToConsole(settings.Highscores.Concat(recent).ToDictionary()); if (lastAttemptOutcome != null) { Console.WriteLine(); ConsoleUtil.WriteLine("Last attempt: " + lastAttemptOutcome); } Console.WriteLine(); Console.WriteLine("Type every letter A-Z exactly once, either alphabetically or in any order. Wait 1 second when done, or press space to restart."); Console.WriteLine(); var pressed = new Dictionary <char, DateTime>(); Console.Title = string.Join(" ", Enumerable.Range('A', 26).Select(x => (char)x).Except(pressed.Keys).OrderBy(x => x)); while (true) { var key = Console.ReadKey(true); Console.Write((key.KeyChar + " ").ToUpper()); if (key.KeyChar < 'a' || key.KeyChar > 'z') { break; } var chr = char.ToUpper(key.KeyChar); if (pressed.ContainsKey(chr)) { break; } pressed[chr] = DateTime.UtcNow; Console.Title = string.Join(" ", Enumerable.Range('A', 26).Select(x => (char)x).Except(pressed.Keys).OrderBy(x => x)); if (pressed.Count == 26) { break; } } Console.WriteLine(); Console.WriteLine('\x7'); if (pressed.Count == 26) { Console.WriteLine("Don't press anything now, to confirm you've typed just the 26 letters accurately and nothing else!"); var wait = DateTime.UtcNow; while (DateTime.UtcNow < wait + TimeSpan.FromSeconds(1) && !Console.KeyAvailable) { Thread.Sleep(10); } } if (pressed.Count == 26 && !Console.KeyAvailable) { UpdateHighscore(highscore, pressed); cur++; UpdateHighscore(recent[$"Recent: {cur:00}"], pressed); if (recent.ContainsKey($"Recent: {cur - 20:00}")) { recent.Remove($"Recent: {cur - 20:00}"); } lastAttemptOutcome = ""; char expected = 'A'; bool alphabetic = true; foreach (var kvp in pressed.OrderBy(kvp => kvp.Value)) { if (kvp.Key != expected) { alphabetic = false; } lastAttemptOutcome += $"{kvp.Key} ".Color(kvp.Key != expected ? ConsoleColor.Red : alphabetic ? ConsoleColor.Green : ConsoleColor.White); expected = (char)(kvp.Key + 1); } lastAttemptOutcome = (alphabetic ? "ALPHABETIC" : "any order") + " " + lastAttemptOutcome; settings.Save(); } else { lastAttemptOutcome = "OOPS!...".Color(ConsoleColor.Magenta); } Console.WriteLine(); Console.WriteLine(); } }
/// <summary>Equivalent to <see cref="IEnumerableExtensions.JoinString{T}"/>, but for <see cref="ConsoleColoredString"/>s.</summary> public static ConsoleColoredString JoinColoredString <T>(this IEnumerable <T> values, ConsoleColoredString separator = null, ConsoleColoredString prefix = null, ConsoleColoredString suffix = null, ConsoleColor defaultColor = ConsoleColor.Gray) { if (values == null) { throw new ArgumentNullException(nameof(values)); } using (var enumerator = values.GetEnumerator()) { if (!enumerator.MoveNext()) { return(ConsoleColoredString.Empty); } var list = new List <ConsoleColoredString>(values is ICollection <T>?((ICollection <T>)values).Count * 4 : 8); bool first = true; do { if (!first && separator != null) { list.Add(separator); } first = false; if (prefix != null) { list.Add(prefix); } if (enumerator.Current != null) { list.Add(enumerator.Current.ToConsoleColoredString(defaultColor)); } if (suffix != null) { list.Add(suffix); } }while (enumerator.MoveNext()); return(new ConsoleColoredString(list)); } }
protected override ExecuteResult ExecuteCore() { var drives = SMART.GetDrivesWithSMART(); var result = new ExecuteResult(); result.ConsoleReport = ""; foreach (var drive in drives) { var driveStatus = Status.Healthy; ConsoleColoredString smartReport = ""; bool any = false; foreach (var smart in drive.SmartReadings) { var reportables = ReportRules.Where(ra => ra.AttributeId == smart.Id && (ra.MediaTypeFilter == null || ra.MediaTypeFilter.Count == 0 || ra.MediaTypeFilter.Contains(drive.MediaType))).ToList(); if (reportables.Count == 0) { continue; } any = true; var statusCur = Status.Healthy; var statusRaw = Status.Healthy; foreach (var reportable in reportables) { if (smart.Worst <= smart.Threshold) { statusCur = statusCur.WorstStatus(reportable.Severity); } if (reportable.RawThreshold != null && smart.Raw >= reportable.RawThreshold) { statusRaw = statusCur.WorstStatus(reportable.Severity); } } driveStatus = driveStatus.WorstStatus(statusCur).WorstStatus(statusRaw); result.UpdateStatus(statusCur.WorstStatus(statusRaw)); var clrCur = statusCur.GetConsoleColor(); var clrRaw = reportables.All(r => r.RawThreshold == null) ? ConsoleColor.Gray : statusRaw.GetConsoleColor(); smartReport += $" {smart.Id:X2} {smart.Name.SubstringSafe(0, 30),-30} : " + smart.Raw.ToString().PadRight(8, ' ').Color(clrRaw) + $" (cur {smart.Current,3}, worst {smart.Worst,3}, thresh {smart.Threshold,3})\r\n".Color(clrCur); } result.ConsoleReport += drive.Model.Color(ConsoleColor.Cyan) + " " + $"{drive.Size / 1_000_000_000.0:#,0} GB ".Color(ConsoleColor.Magenta) + drive.MediaType.ToString().Color(ConsoleColor.White) + $" s/n: {drive.SerialNumber}\r\n"; result.ConsoleReport += "SMART prediction: " + (drive.SmartPredictFailure ? "FAILURE IMMINENT".Color(ConsoleColor.Red) : "Healthy".Color(ConsoleColor.Green)) + "\r\n"; if (drive.SmartPredictFailure) { result.UpdateStatus(Status.RedAlert); } if (any) { result.ConsoleReport += "Our prediction: " + driveStatus.ToString().Color(driveStatus.GetConsoleColor()) + "\r\n\r\n"; result.ConsoleReport += smartReport; } else { result.ConsoleReport += "\r\n No relevant SMART attributes found for this drive.\r\n".Color(ConsoleColor.Yellow); result.UpdateStatus(Status.Warning); } result.ConsoleReport += "\r\n\r\n"; } return(result); }