Forge
csharpdeeb25a2
1#nullable enable
2using System.Text.Json;
3using System.Text.Json.Serialization;
4
5namespace CascadeIDE.Services;
6
7/// <summary>Слияние именованного пресета из JSON настроек с аргументами MCP.</summary>
8public static class CodeNavigationPresetMerge
9{
10 private sealed class PresetDto
11 {
12 [JsonPropertyName("include_kinds")]
13 public List<string>? IncludeKinds { get; set; }
14
15 [JsonPropertyName("exclude_kinds")]
16 public List<string>? ExcludeKinds { get; set; }
17 }
18
19 /// <summary>
20 /// <paramref name="requestInclude"/> / <paramref name="requestExclude"/> с не-<c>null</c> перезаписывают соответствующую сторону пресета;
21 /// для exclude пресет и запрос объединяются, если оба заданы (дедуп по канону).
22 /// </summary>
23 public static (IReadOnlyList<string>? Include, IReadOnlyList<string>? Exclude, string? Error) Merge(
24 string? presetName,
25 string presetsJson,
26 IReadOnlyList<string>? requestInclude,
27 IReadOnlyList<string>? requestExclude)
28 {
29 IReadOnlyList<string>? pInc = null;
30 IReadOnlyList<string>? pExc = null;
31 if (!string.IsNullOrWhiteSpace(presetName))
32 {
33 if (!TryGetPreset(presetsJson, presetName.Trim(), out pInc, out pExc, out var err))
34 return (null, null, err);
35 }
36
37 var inc = requestInclude ?? pInc;
38
39 if (requestExclude is not null && requestExclude.Count > 0)
40 {
41 if (pExc is { Count: > 0 })
42 {
43 var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
44 foreach (var x in pExc)
45 {
46 var c = CodeNavigationRelatedKinds.TryCanonicalKind(x);
47 if (c is not null)
48 set.Add(c);
49 }
50
51 foreach (var x in requestExclude)
52 {
53 var c = CodeNavigationRelatedKinds.TryCanonicalKind(x);
54 if (c is not null)
55 set.Add(c);
56 }
57
58 return (inc, set.OrderBy(x => x, StringComparer.Ordinal).ToList(), null);
59 }
60
61 return (inc, requestExclude, null);
62 }
63
64 return (inc, pExc ?? Array.Empty<string>(), null);
65 }
66
67 private static bool TryGetPreset(
68 string presetsJson,
69 string key,
70 out IReadOnlyList<string>? includeKinds,
71 out IReadOnlyList<string>? excludeKinds,
72 out string? error)
73 {
74 includeKinds = null;
75 excludeKinds = null;
76 error = null;
77 try
78 {
79 var dict = JsonSerializer.Deserialize<Dictionary<string, PresetDto>>(presetsJson.Trim());
80 if (dict is null || !dict.TryGetValue(key, out var dto) || dto is null)
81 {
82 error = $"Неизвестный пресет «{key}»";
83 return false;
84 }
85
86 if (dto.IncludeKinds is { Count: > 0 })
87 {
88 var list = new List<string>();
89 foreach (var t in dto.IncludeKinds)
90 {
91 var c = CodeNavigationRelatedKinds.TryCanonicalKind(t);
92 if (c is not null)
93 list.Add(c);
94 }
95
96 includeKinds = list.Count > 0 ? list : null;
97 }
98
99 if (dto.ExcludeKinds is { Count: > 0 })
100 {
101 var list = new List<string>();
102 foreach (var t in dto.ExcludeKinds)
103 {
104 var c = CodeNavigationRelatedKinds.TryCanonicalKind(t);
105 if (c is not null)
106 list.Add(c);
107 }
108
109 excludeKinds = list.Count > 0 ? list : null;
110 }
111
112 return true;
113 }
114 catch (Exception ex)
115 {
116 error = $"presets_json: {ex.Message}";
117 return false;
118 }
119 }
120}
121
View only · write via MCP/CIDE