| 1 | using CascadeIDE.Contracts; |
| 2 | |
| 3 | namespace CascadeIDE.Cockpit.ComputingUnits.IdeHealth; |
| 4 | |
| 5 | /// <summary> |
| 6 | /// CCU «текст из скаляров» (реализует <see cref="ICockpitComputeUnit"/>, ADR 0097): чистое форматирование IDE Health без VM/DAP. |
| 7 | /// Собирает <see cref="IdeHealthSegmentInput"/>; живые зависимости — в <see cref="IdeHealthSnapshotUnit"/>. |
| 8 | /// </summary> |
| 9 | [ComputingUnit] |
| 10 | public sealed class IdeHealthFormattingUnit : ICockpitComputeUnit |
| 11 | { |
| 12 | /// <summary>Единственный экземпляр свёртки (без состояния).</summary> |
| 13 | public static IdeHealthFormattingUnit Default { get; } = new(); |
| 14 | |
| 15 | private IdeHealthFormattingUnit() |
| 16 | { |
| 17 | } |
| 18 | |
| 19 | public IdeHealthSegmentInput BuildSegment(bool isBuilding) => |
| 20 | BuildSegment(new BuildStateSnapshot(isBuilding)); |
| 21 | |
| 22 | public IdeHealthSegmentInput BuildSegment(BuildStateSnapshot s) |
| 23 | { |
| 24 | if (s.IsBuilding) |
| 25 | { |
| 26 | return new IdeHealthSegmentInput( |
| 27 | "Build: running…", |
| 28 | "BUILD…", |
| 29 | IsBuildRunning: true, |
| 30 | Stratum: IdeHealthStratum.Solution, |
| 31 | Scope: IdeHealthScope.Solution); |
| 32 | } |
| 33 | |
| 34 | var line = s.LastBuildSucceeded switch |
| 35 | { |
| 36 | true when s.LastExitCode is int ok => $"Build: idle · last OK (exit {ok})", |
| 37 | true => "Build: idle · last OK", |
| 38 | false when s.LastExitCode is int fail => $"Build: idle · last failed (exit {fail})", |
| 39 | false => "Build: idle · last failed", |
| 40 | _ => "Build: idle" |
| 41 | }; |
| 42 | return new IdeHealthSegmentInput( |
| 43 | line, |
| 44 | "READY", |
| 45 | IsBuildRunning: false, |
| 46 | Stratum: IdeHealthStratum.Solution, |
| 47 | Scope: IdeHealthScope.Solution); |
| 48 | } |
| 49 | |
| 50 | public IdeHealthSegmentInput TestsSegment(string? lastTestSummary, int impactedTestsBadge) |
| 51 | { |
| 52 | var line = !string.IsNullOrWhiteSpace(lastTestSummary) |
| 53 | ? $"Tests: {lastTestSummary}" |
| 54 | : $"Tests: impacted {impactedTestsBadge}"; |
| 55 | var cockpit = !string.IsNullOrWhiteSpace(lastTestSummary) |
| 56 | ? (lastTestSummary.Length > 36 ? string.Concat(lastTestSummary.AsSpan(0, 33), "…") : lastTestSummary) |
| 57 | : $"imp {impactedTestsBadge}"; |
| 58 | return new IdeHealthSegmentInput(line, cockpit, Stratum: IdeHealthStratum.Solution, Scope: IdeHealthScope.Solution); |
| 59 | } |
| 60 | |
| 61 | public IdeHealthSegmentInput DebugSegment( |
| 62 | bool hasActiveSession, |
| 63 | bool executionStopped, |
| 64 | int stackFrameCount, |
| 65 | int variableCount) |
| 66 | { |
| 67 | if (!hasActiveSession) |
| 68 | return new IdeHealthSegmentInput("Debug: idle", "DBG · —", Stratum: IdeHealthStratum.Solution, Scope: IdeHealthScope.Solution); |
| 69 | |
| 70 | if (executionStopped) |
| 71 | { |
| 72 | var line = $"Debug: paused · frames {stackFrameCount}, vars {variableCount}"; |
| 73 | var shortLine = $"DBG · pause · {stackFrameCount}fr"; |
| 74 | return new IdeHealthSegmentInput(line, shortLine, Stratum: IdeHealthStratum.Solution, Scope: IdeHealthScope.Solution); |
| 75 | } |
| 76 | |
| 77 | return new IdeHealthSegmentInput("Debug: running…", "DBG · run", Stratum: IdeHealthStratum.Solution, Scope: IdeHealthScope.Solution); |
| 78 | } |
| 79 | |
| 80 | public IdeHealthSegmentInput GitSegment(string gitLine, string gitCockpitShort) => |
| 81 | new(gitLine, gitCockpitShort, Stratum: IdeHealthStratum.Workspace); |
| 82 | |
| 83 | public IdeHealthSegmentInput ProjectBuildSegment(string projectPath, bool isBuilding) => |
| 84 | ProjectBuildSegment(projectPath, new BuildStateSnapshot(isBuilding)); |
| 85 | |
| 86 | public IdeHealthSegmentInput ProjectBuildSegment(string projectPath, BuildStateSnapshot s) |
| 87 | { |
| 88 | if (s.IsBuilding) |
| 89 | { |
| 90 | return new IdeHealthSegmentInput( |
| 91 | $"Build[{projectPath}]: running…", |
| 92 | "BUILD…", |
| 93 | IsBuildRunning: true, |
| 94 | Stratum: IdeHealthStratum.Solution, |
| 95 | Scope: IdeHealthScope.Project, |
| 96 | ProjectPath: projectPath); |
| 97 | } |
| 98 | |
| 99 | var tail = s.LastBuildSucceeded switch |
| 100 | { |
| 101 | true when s.LastExitCode is int ok => $"idle · last OK (exit {ok})", |
| 102 | true => "idle · last OK", |
| 103 | false when s.LastExitCode is int fail => $"idle · last failed (exit {fail})", |
| 104 | false => "idle · last failed", |
| 105 | _ => "idle" |
| 106 | }; |
| 107 | return new IdeHealthSegmentInput( |
| 108 | $"Build[{projectPath}]: {tail}", |
| 109 | "READY", |
| 110 | IsBuildRunning: false, |
| 111 | Stratum: IdeHealthStratum.Solution, |
| 112 | Scope: IdeHealthScope.Project, |
| 113 | ProjectPath: projectPath); |
| 114 | } |
| 115 | |
| 116 | public IdeHealthSegmentInput ProjectTestsSegment(string projectPath, string? summary, int impactedTestsBadge) |
| 117 | { |
| 118 | var normalizedSummary = string.IsNullOrWhiteSpace(summary) |
| 119 | ? $"impacted {impactedTestsBadge}" |
| 120 | : summary; |
| 121 | return new IdeHealthSegmentInput( |
| 122 | $"Tests[{projectPath}]: {normalizedSummary}", |
| 123 | normalizedSummary.Length > 36 ? string.Concat(normalizedSummary.AsSpan(0, 33), "…") : normalizedSummary, |
| 124 | Stratum: IdeHealthStratum.Solution, |
| 125 | Scope: IdeHealthScope.Project, |
| 126 | ProjectPath: projectPath); |
| 127 | } |
| 128 | |
| 129 | public IdeHealthSegmentInput ProjectDebugSegment(string projectPath, string summary) => |
| 130 | new( |
| 131 | $"Debug[{projectPath}]: {summary}", |
| 132 | summary.Length > 36 ? string.Concat(summary.AsSpan(0, 33), "…") : summary, |
| 133 | Stratum: IdeHealthStratum.Solution, |
| 134 | Scope: IdeHealthScope.Project, |
| 135 | ProjectPath: projectPath); |
| 136 | |
| 137 | /// <summary>Собирает снимок из уже вычисленных скаляров (удобно для тестов и провайдера).</summary> |
| 138 | public IdeHealthInputSnapshot Compose( |
| 139 | BuildStateSnapshot buildState, |
| 140 | string? lastTestSummary, |
| 141 | int impactedTestsBadge, |
| 142 | bool hasDebugSession, |
| 143 | bool debugExecutionStopped, |
| 144 | int debugStackFrameCount, |
| 145 | int debugVariableCount, |
| 146 | string gitLine, |
| 147 | string gitCockpitShort) => |
| 148 | IdeHealthStrataComposer.Compose( |
| 149 | new IdeHealthWorkspaceInput(GitSegment(gitLine, gitCockpitShort)), |
| 150 | new IdeHealthSolutionInput( |
| 151 | BuildSegment(buildState), |
| 152 | TestsSegment(lastTestSummary, impactedTestsBadge), |
| 153 | DebugSegment(hasDebugSession, debugExecutionStopped, debugStackFrameCount, debugVariableCount))); |
| 154 | } |
| 155 | |