Forge
csharpdeeb25a2
1using CascadeIDE.Models.Forge;
2
3namespace CascadeIDE.Features.Forge.Infrastructure;
4
5/// <summary>Parse <c>[FRG:repo/issues/N]</c> and optional compound code tail (ADR-0159).</summary>
6public static class BracketForgeReferenceParser
7{
8 public static bool TryParse(string? input, out ForgeArtifactRef reference, out string error)
9 {
10 reference = default!;
11 error = "";
12
13 var text = (input ?? "").Trim();
14 if (text.Length == 0)
15 {
16 error = "Пустая bracket-ссылка.";
17 return false;
18 }
19
20 if (text.StartsWith('[') && text.EndsWith(']'))
21 text = text[1..^1].Trim();
22
23 if (!text.StartsWith("FRG:", StringComparison.Ordinal))
24 {
25 error = "Forge bracket must start with FRG:.";
26 return false;
27 }
28
29 text = text["FRG:".Length..].Trim();
30 var semi = text.IndexOf(';');
31 var path = semi >= 0 ? text[..semi].Trim() : text;
32 var codeTail = semi >= 0 ? text[(semi + 1)..].Trim() : null;
33
34 if (!TryParsePath(path, out var repo, out var kind, out var number, out error))
35 return false;
36
37 reference = new ForgeArtifactRef(repo, kind, number, string.IsNullOrWhiteSpace(codeTail) ? null : codeTail);
38 return true;
39 }
40
41 private static bool TryParsePath(string path, out string repo, out ForgeArtifactKind kind, out int number, out string error)
42 {
43 repo = "";
44 kind = ForgeArtifactKind.Issue;
45 number = 0;
46 error = "";
47
48 var parts = path.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
49 if (parts.Length == 2 && parts[1].Equals("repos", StringComparison.OrdinalIgnoreCase))
50 {
51 repo = parts[0];
52 kind = ForgeArtifactKind.Repo;
53 number = 0;
54 return repo.Length > 0;
55 }
56
57 if (parts.Length != 3)
58 {
59 error = "Ожидается FRG:repo/issues/N или FRG:repo/mr/N.";
60 return false;
61 }
62
63 repo = parts[0];
64 if (parts[1].Equals("issues", StringComparison.OrdinalIgnoreCase))
65 kind = ForgeArtifactKind.Issue;
66 else if (parts[1].Equals("mr", StringComparison.OrdinalIgnoreCase))
67 kind = ForgeArtifactKind.MergeRequest;
68 else
69 {
70 error = "Kind must be issues or mr.";
71 return false;
72 }
73
74 if (!int.TryParse(parts[2], out number) || number <= 0)
75 {
76 error = "Номер issue/MR должен быть > 0.";
77 return false;
78 }
79
80 return repo.Length > 0;
81 }
82}
83
View only · write via MCP/CIDE