using System.Text.Json; namespace VideoDownloader.Browser; /// T3-1: 도메인 → 워크스페이스 자동 라우팅 규칙(Vivaldi 규칙/Arc ATC). /// 주소창 이동 시 매칭되면 해당 워크스페이스로 자동 전환한다. JSON 영속. public sealed class WorkspaceRoutingRules { private readonly string _path; private Dictionary _map = new(StringComparer.OrdinalIgnoreCase); public WorkspaceRoutingRules(string path) { _path = path; try { if (File.Exists(path)) _map = JsonSerializer.Deserialize>(File.ReadAllText(path)) ?? new(); } catch { } _map ??= new Dictionary(StringComparer.OrdinalIgnoreCase); } public IReadOnlyDictionary Rules => _map; public void Add(string domain, string workspaceName) { var d = domain.Trim().TrimStart('.', 'w', 'w', 'w', '.'); _map[d] = workspaceName; Save(); } public void Remove(string domain) { _map.Remove(domain.Trim()); Save(); } /// URL 이 매칭되는 워크스페이스명(없으면 null). www/서브도메인 허용. public string? Match(string? url) { if (string.IsNullOrWhiteSpace(url)) return null; if (!Uri.TryCreate(url, UriKind.Absolute, out var u) || (u.Scheme != Uri.UriSchemeHttp && u.Scheme != Uri.UriSchemeHttps)) return null; var host = u.Host; if (host.StartsWith("www.", StringComparison.OrdinalIgnoreCase)) host = host[4..]; foreach (var (domain, ws) in _map) { if (host.Equals(domain, StringComparison.OrdinalIgnoreCase) || host.EndsWith("." + domain, StringComparison.OrdinalIgnoreCase)) return ws; } return null; } private void Save() { try { Directory.CreateDirectory(Path.GetDirectoryName(_path)!); File.WriteAllText(_path, JsonSerializer.Serialize(_map)); } catch { } } }