< Summary

Information
Class: NexusLabs.Needlr.Generators.BreadcrumbWriter
Assembly: NexusLabs.Needlr.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/BreadcrumbWriter.cs
Line coverage
91%
Covered lines: 43
Uncovered lines: 4
Coverable lines: 47
Total lines: 126
Line coverage: 91.4%
Branch coverage
83%
Covered branches: 25
Total branches: 30
Branch coverage: 83.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Level()100%11100%
.ctor(...)100%11100%
WriteInlineComment(...)100%22100%
WriteVerboseBox(...)66.66%7675%
WriteFileHeader(...)100%22100%
GetRelativeSourcePath(...)88.88%1818100%
PadRight(...)50%2266.66%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/BreadcrumbWriter.cs

#LineLine coverage
 1using System.Text;
 2
 3namespace NexusLabs.Needlr.Generators;
 4
 5/// <summary>
 6/// Helper class for writing documentation breadcrumbs to generated source code.
 7/// Respects the configured <see cref="BreadcrumbLevel"/> to control output verbosity.
 8/// </summary>
 9internal sealed class BreadcrumbWriter
 10{
 11    private const int BoxWidth = 73;
 12    private const string BoxTopLeft = "┌";
 13    private const string BoxTopRight = "┐";
 14    private const string BoxBottomLeft = "└";
 15    private const string BoxBottomRight = "┘";
 16    private const string BoxVertical = "│";
 17    private const string BoxHorizontal = "─";
 18
 29202819    public BreadcrumbLevel Level { get; }
 20
 43421    public BreadcrumbWriter(BreadcrumbLevel level)
 22    {
 43423        Level = level;
 43424    }
 25
 26    /// <summary>
 27    /// Writes a simple inline comment. Only outputs at Minimal or Verbose level.
 28    /// </summary>
 29    public void WriteInlineComment(StringBuilder sb, string indent, string comment)
 30    {
 2976331        if (Level == BreadcrumbLevel.None)
 118732            return;
 33
 2857634        sb.AppendLine($"{indent}// {comment}");
 2857635    }
 36
 37    /// <summary>
 38    /// Writes a verbose box comment with multiple lines. Only outputs at Verbose level.
 39    /// At Minimal level, outputs a simple inline comment with the title.
 40    /// </summary>
 41    public void WriteVerboseBox(StringBuilder sb, string indent, string title, params string[] lines)
 42    {
 1522643        if (Level == BreadcrumbLevel.None)
 044            return;
 45
 1522646        if (Level == BreadcrumbLevel.Minimal)
 47        {
 048            sb.AppendLine($"{indent}// {title}");
 049            return;
 50        }
 51
 52        // Verbose: Full box
 1522653        var horizontalLine = new string('─', BoxWidth);
 1522654        sb.AppendLine($"{indent}// {BoxTopLeft}{horizontalLine}{BoxTopRight}");
 1522655        sb.AppendLine($"{indent}// {BoxVertical} {PadRight(title, BoxWidth - 1)}{BoxVertical}");
 56
 9156657        foreach (var line in lines)
 58        {
 3055759            sb.AppendLine($"{indent}// {BoxVertical} {PadRight(line, BoxWidth - 1)}{BoxVertical}");
 60        }
 61
 1522662        sb.AppendLine($"{indent}// {BoxBottomLeft}{horizontalLine}{BoxBottomRight}");
 1522663    }
 64
 65    /// <summary>
 66    /// Writes the file header with breadcrumb level indicator. Only at Verbose level.
 67    /// </summary>
 68    public void WriteFileHeader(StringBuilder sb, string assemblyName, string fileDescription)
 69    {
 96970        sb.AppendLine("// <auto-generated/>");
 71
 96972        if (Level == BreadcrumbLevel.Verbose)
 73        {
 5974            sb.AppendLine("// ═══════════════════════════════════════════════════════════════════════════════");
 5975            sb.AppendLine($"// {fileDescription}");
 5976            sb.AppendLine($"// Assembly: {assemblyName}");
 5977            sb.AppendLine($"// Breadcrumb Level: {Level}");
 5978            sb.AppendLine("// ═══════════════════════════════════════════════════════════════════════════════");
 79        }
 96980    }
 81
 82    /// <summary>
 83    /// Gets a relative path from an absolute path, using the project directory as base.
 84    /// Falls back to just the filename if project root is unavailable.
 85    /// </summary>
 86    public static string GetRelativeSourcePath(string? absolutePath, string? projectDirectory)
 87    {
 4554188        if (absolutePath == null || absolutePath.Length == 0)
 4553589            return "[unknown]";
 90
 91        // Normalize path separators for cross-platform compatibility
 692        var normalizedPath = absolutePath.Replace("\\", "/");
 93
 694        if (projectDirectory == null || projectDirectory.Length == 0)
 95        {
 96            // Fallback to just the filename - use LastIndexOf for cross-platform
 297            var lastSlash = normalizedPath.LastIndexOf('/');
 298            return lastSlash >= 0 ? normalizedPath.Substring(lastSlash + 1) : normalizedPath;
 99        }
 100
 4101        var normalizedProjectDir = projectDirectory.Replace("\\", "/");
 102        // Ensure project dir doesn't end with slash for consistent comparison
 4103        if (normalizedProjectDir.EndsWith("/"))
 1104            normalizedProjectDir = normalizedProjectDir.Substring(0, normalizedProjectDir.Length - 1);
 105
 106        // Try to make it relative
 4107        if (normalizedPath.StartsWith(normalizedProjectDir, StringComparison.OrdinalIgnoreCase))
 108        {
 3109            var relative = normalizedPath.Substring(normalizedProjectDir.Length);
 3110            if (relative.StartsWith("/"))
 3111                relative = relative.Substring(1);
 3112            return relative;
 113        }
 114
 115        // Couldn't make relative, just return filename
 1116        var lastSlashFallback = normalizedPath.LastIndexOf('/');
 1117        return lastSlashFallback >= 0 ? normalizedPath.Substring(lastSlashFallback + 1) : normalizedPath;
 118    }
 119
 120    private static string PadRight(string text, int totalWidth)
 121    {
 45783122        if (text.Length >= totalWidth)
 0123            return text.Substring(0, totalWidth);
 45783124        return text + new string(' ', totalWidth - text.Length);
 125    }
 126}