namespace Avalonia_Services.Core { /// /// Matches unified endpoint patterns and extracts simple route values. /// internal static class ServiceEndpointPatternMatcher { /// /// Match literal segments and single-segment route parameters such as {id} or {id:int}. /// public static bool TryMatch( string pattern, string path, out Dictionary routeValues) { routeValues = new Dictionary(StringComparer.OrdinalIgnoreCase); var patternSegments = SplitSegments(pattern); var pathSegments = SplitSegments(path); if (patternSegments.Length != pathSegments.Length) { return false; } for (var index = 0; index < patternSegments.Length; index++) { var patternSegment = patternSegments[index]; var pathSegment = pathSegments[index]; if (TryGetParameterName(patternSegment, out var parameterName)) { if (!MatchesConstraint(patternSegment, pathSegment)) { return false; } routeValues[parameterName] = Uri.UnescapeDataString(pathSegment); continue; } if (!string.Equals(patternSegment, pathSegment, StringComparison.OrdinalIgnoreCase)) { return false; } } return true; } private static string[] SplitSegments(string value) { return value.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); } private static bool TryGetParameterName(string segment, out string parameterName) { parameterName = string.Empty; if (segment.Length < 3 || segment[0] != '{' || segment[^1] != '}') { return false; } var token = segment[1..^1]; var constraintIndex = token.IndexOf(':'); parameterName = constraintIndex >= 0 ? token[..constraintIndex] : token; return !string.IsNullOrWhiteSpace(parameterName); } private static bool MatchesConstraint(string segment, string value) { return !segment.EndsWith(":int}", StringComparison.OrdinalIgnoreCase) || int.TryParse(value, out _); } } }