using System; using System.Collections.Generic; using System.Text; using MergeHub.Models; namespace MergeHub.Services { /// /// 비교 결과를 메모장으로 열어 읽을 수 있는 글로 만든다. /// /// 왜 글로 뽑나 - 결과를 남에게 보내거나 결재에 붙일 때 화면을 캡처하는 것 말고는 방법이 없었다. /// 캡처는 검색도 안 되고 줄 번호를 짚어 말하기도 어렵다. /// /// 화면 없이 검사할 수 있어야 하므로 글을 만드는 일만 여기서 한다. /// 어디에 저장할지 묻는 것은 화면 쪽 일이다. /// public static class CompareReport { private const string Rule = "----------------------------------------------------------------"; /// 파일 비교 결과. public static string ForFile(string leftLabel, string rightLabel, IReadOnlyList lines, DateTime when, string? author = null) { var text = new StringBuilder(); Head(text, "파일 비교 결과", leftLabel, rightLabel, when, author); var changed = 0; var added = 0; var removed = 0; foreach (var line in lines) { if (line.IsMergeNote || line.IsIgnored) continue; switch (line.State) { case MergeLineState.Changed: changed++; break; case MergeLineState.LocalOnly: added++; break; case MergeLineState.BaseOnly: removed++; break; } } text.AppendLine($"요약: 총 {lines.Count} 줄 · 다름 {changed} · 오른쪽에만 {added} · 왼쪽에만 {removed}"); text.AppendLine(); if (changed + added + removed == 0) { text.AppendLine("다른 줄이 없습니다."); return text.ToString(); } text.AppendLine("다른 줄"); text.AppendLine(Rule); foreach (var line in lines) { if (line.IsMergeNote || line.IsIgnored) continue; if (line.State == MergeLineState.Same) continue; // 줄 번호는 양쪽을 다 적는다. 한쪽에만 있는 줄이 있어 번호가 어긋나므로, // 하나만 적으면 파일에서 그 줄을 찾을 수 없다. var number = $"{Number(line.BaseNumber)}/{Number(line.LocalNumber)}"; text.AppendLine($"[{number}] {StateName(line.State)}"); if (line.HasBase) text.AppendLine(" - " + line.BaseText); if (line.HasLocal) text.AppendLine(" + " + line.LocalText); } return text.ToString(); } /// 폴더 비교 결과. public static string ForFolder(string leftFolder, string rightFolder, FolderCompareResult result, DateTime when, string? author = null) { var text = new StringBuilder(); Head(text, "폴더 비교 결과", leftFolder, rightFolder, when, author); text.AppendLine("요약: " + result.Summary); if (result.Truncated) text.AppendLine("(항목이 너무 많아 도중에 멈춘 결과입니다)"); text.AppendLine(); var rows = new List<(string Status, string Path)>(); Collect(result.Items, rows); if (rows.Count == 0) { text.AppendLine("다른 항목이 없습니다."); return text.ToString(); } text.AppendLine("다른 항목"); text.AppendLine(Rule); foreach (var row in rows) text.AppendLine($"{row.Status,-12}{row.Path}"); return text.ToString(); } /// 폴더 트리를 훑어 "같음"이 아닌 파일만 모은다. private static void Collect(IEnumerable items, List<(string, string)> rows) { foreach (var item in items) { if (item.IsDirectory) { Collect(item.Children, rows); continue; } if (item.Status == FolderEntryStatus.Same) continue; rows.Add((StatusName(item.Status), item.RelativePath)); } } /// /// 뽑은 사람. 남에게 보내라고 만든 글이라 누가 뽑았는지가 없으면 받는 쪽이 되묻게 된다. /// 비어 있으면 줄 자체를 넣지 않는다 - "만든 사람: " 만 덩그러니 있는 것보다 없는 편이 낫다. /// private static void Head(StringBuilder text, string title, string left, string right, DateTime when, string? author) { text.AppendLine($"MergeHub {title}"); text.AppendLine($"만든 때: {when:yyyy-MM-dd HH:mm:ss}"); if (!string.IsNullOrWhiteSpace(author)) text.AppendLine($"만든 사람: {author}"); text.AppendLine($"왼쪽 : {left}"); text.AppendLine($"오른쪽: {right}"); text.AppendLine(Rule); } private static string Number(int? value) => value is int n ? n.ToString() : "-"; private static string StateName(MergeLineState state) => state switch { MergeLineState.Changed => "다름", MergeLineState.LocalOnly => "오른쪽에만", MergeLineState.BaseOnly => "왼쪽에만", _ => "같음" }; private static string StatusName(FolderEntryStatus status) => status switch { FolderEntryStatus.Different => "다름", FolderEntryStatus.CommentOnly => "주석만 다름", FolderEntryStatus.Binary => "다름(이진)", FolderEntryStatus.LeftOnly => "왼쪽에만", FolderEntryStatus.RightOnly => "오른쪽에만", _ => "같음" }; } }