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 => "오른쪽에만",
_ => "같음"
};
}
}