Forge
csharpdeeb25a2
1#nullable enable
2
3using System.Globalization;
4using CascadeIDE.Services;
5using CascadeIDE.Services.Intercom;
6
7namespace CascadeIDE.Features.Chat.AnchorPeek;
8
9internal static class AnchorPeekTargetParser
10{
11 public const string EmptyTargetError =
12 "Укажи № якоря (1…) или 8 hex: /anchor peek 1 или /anchor peek abcd1234.";
13
14 public const string InvalidHexError =
15 "Id якоря — 8 hex-символов (как в маркере ⟦a:abcd1234⟧).";
16
17 public static bool TryParse(string? raw, out AnchorPeekTarget target, out string error)
18 {
19 target = default;
20 error = "";
21 var t = (raw ?? "").Trim();
22 if (t.Length == 0)
23 {
24 error = EmptyTargetError;
25 return false;
26 }
27
28 if (TryParseOrdinalToken(t, out var ordinal))
29 {
30 target = AnchorPeekTarget.FromOrdinal(ordinal);
31 return true;
32 }
33
34 if (TryParseHexToken(t, out var hexId, out error))
35 {
36 target = AnchorPeekTarget.FromHexId(hexId);
37 return true;
38 }
39
40 return false;
41 }
42
43 public static bool IsPartialHex(string raw)
44 {
45 var t = raw;
46 if (t.StartsWith("a:", StringComparison.OrdinalIgnoreCase))
47 t = t[2..].Trim();
48
49 return t.Length > 0
50 && t.Length < 8
51 && t.All(static c => c is >= '0' and <= '9' or >= 'a' and <= 'f' or >= 'A' and <= 'F');
52 }
53
54 public static bool LooksLikeHexEntry(string token)
55 {
56 if (token.StartsWith("a:", StringComparison.OrdinalIgnoreCase))
57 return true;
58
59 if (token.Any(static c => c is >= 'a' and <= 'f' or >= 'A' and <= 'F'))
60 return true;
61
62 return token.Length >= 8
63 && token.All(static c => c is >= '0' and <= '9' or >= 'a' and <= 'f' or >= 'A' and <= 'F');
64 }
65
66 private static bool TryParseOrdinalToken(string t, out int ordinal)
67 {
68 ordinal = 0;
69 if (t.StartsWith("a:", StringComparison.OrdinalIgnoreCase))
70 return false;
71
72 if (!t.All(static c => c is >= '0' and <= '9'))
73 return false;
74
75 return int.TryParse(t, NumberStyles.None, CultureInfo.InvariantCulture, out ordinal) && ordinal >= 1;
76 }
77
78 private static bool TryParseHexToken(string t, out string hexId, out string error)
79 {
80 hexId = "";
81 error = "";
82 if (t.StartsWith("a:", StringComparison.OrdinalIgnoreCase))
83 t = t[2..].Trim();
84
85 var wirePrefix = $"{IntercomAttachmentMarkers.MarkerOpen}a:";
86 if (t.StartsWith(wirePrefix, StringComparison.Ordinal))
87 {
88 var end = t.IndexOf(IntercomAttachmentMarkers.MarkerClose);
89 t = end >= wirePrefix.Length ? t[wirePrefix.Length..end] : t[wirePrefix.Length..];
90 }
91
92 if (t.Length != 8 || !t.All(static c => c is >= '0' and <= '9' or >= 'a' and <= 'f' or >= 'A' and <= 'F'))
93 {
94 error = InvalidHexError;
95 return false;
96 }
97
98 hexId = t.ToLowerInvariant();
99 return true;
100 }
101}
102
View only · write via MCP/CIDE