Forge
csharp4405de34
1#nullable enable
2
3using System.Collections.Concurrent;
4
5namespace CascadeIDE.Services.Intercom;
6
7/// <summary>In-memory parse/model cache per solution scope (ADR 0135 L1).</summary>
8public static class IntercomAttachmentRoslynWorkspaceCache
9{
10 private sealed class ScopeCache
11 {
12 internal readonly ConcurrentDictionary<string, CachedFile> Files = new(StringComparer.OrdinalIgnoreCase);
13 }
14
15 private sealed record CachedFile(long LastWriteUtcTicks, IntercomAttachmentRoslynResolveSession.FileEntry Entry);
16
17 private static readonly ConcurrentDictionary<string, ScopeCache> Scopes = new(StringComparer.Ordinal);
18
19 public static bool TryGet(
20 string scopeKey,
21 string absoluteFilePath,
22 out IntercomAttachmentRoslynResolveSession.FileEntry entry)
23 {
24 entry = null!;
25 if (string.IsNullOrWhiteSpace(scopeKey) || string.IsNullOrWhiteSpace(absoluteFilePath))
26 return false;
27
28 if (!Scopes.TryGetValue(scopeKey, out var scope))
29 return false;
30
31 if (!scope.Files.TryGetValue(absoluteFilePath, out var cached))
32 return false;
33
34 if (!File.Exists(absoluteFilePath))
35 {
36 scope.Files.TryRemove(absoluteFilePath, out _);
37 return false;
38 }
39
40 long ticks;
41 try
42 {
43 ticks = File.GetLastWriteTimeUtc(absoluteFilePath).Ticks;
44 }
45 catch
46 {
47 return false;
48 }
49
50 if (cached.LastWriteUtcTicks != ticks)
51 {
52 scope.Files.TryRemove(absoluteFilePath, out _);
53 return false;
54 }
55
56 entry = cached.Entry;
57 return true;
58 }
59
60 public static void Store(
61 string scopeKey,
62 string absoluteFilePath,
63 IntercomAttachmentRoslynResolveSession.FileEntry entry)
64 {
65 if (string.IsNullOrWhiteSpace(scopeKey) || string.IsNullOrWhiteSpace(absoluteFilePath))
66 return;
67
68 long ticks;
69 try
70 {
71 ticks = File.GetLastWriteTimeUtc(absoluteFilePath).Ticks;
72 }
73 catch
74 {
75 return;
76 }
77
78 var scope = Scopes.GetOrAdd(scopeKey, static _ => new ScopeCache());
79 scope.Files[absoluteFilePath] = new CachedFile(ticks, entry);
80 }
81
82 public static void InvalidateFile(string scopeKey, string absoluteFilePath)
83 {
84 if (string.IsNullOrWhiteSpace(scopeKey) || string.IsNullOrWhiteSpace(absoluteFilePath))
85 return;
86
87 if (Scopes.TryGetValue(scopeKey, out var scope))
88 scope.Files.TryRemove(absoluteFilePath, out _);
89 }
90
91 public static void ClearScope(string scopeKey)
92 {
93 if (string.IsNullOrWhiteSpace(scopeKey))
94 return;
95 Scopes.TryRemove(scopeKey, out _);
96 }
97}
98
View only · write via MCP/CIDE