Got at least one data fetching method working; turns out, we can't use a patched LogicStack to get the data
This commit is contained in:
183
Vendor/EmbedIO-3.5.2/Routing/RouteMatcher.cs
vendored
Normal file
183
Vendor/EmbedIO-3.5.2/Routing/RouteMatcher.cs
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using EmbedIO.Utilities;
|
||||
using Swan;
|
||||
|
||||
namespace EmbedIO.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Matches URL paths against a route.
|
||||
/// </summary>
|
||||
public sealed class RouteMatcher : IEquatable<RouteMatcher>
|
||||
{
|
||||
private static readonly object SyncRoot = new object();
|
||||
private static readonly Dictionary<(bool, string), RouteMatcher> Cache = new Dictionary<(bool, string), RouteMatcher>();
|
||||
|
||||
private readonly Regex _regex;
|
||||
|
||||
private RouteMatcher(bool isBaseRoute, string route, string pattern, IReadOnlyList<string> parameterNames)
|
||||
{
|
||||
IsBaseRoute = isBaseRoute;
|
||||
Route = route;
|
||||
ParameterNames = parameterNames;
|
||||
_regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.CultureInvariant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the <see cref="Route"/> property
|
||||
/// is a base route.
|
||||
/// </summary>
|
||||
public bool IsBaseRoute { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the route this instance matches URL paths against.
|
||||
/// </summary>
|
||||
public string Route { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of the route's parameters.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> ParameterNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an instance of <see cref="RouteMatcher"/> by parsing the specified route.
|
||||
/// <para>If the same route was previously parsed and the <see cref="ClearCache"/> method has not been called since,
|
||||
/// this method obtains an instance from a static cache.</para>
|
||||
/// </summary>
|
||||
/// <param name="route">The route to parse.</param>
|
||||
/// <param name="isBaseRoute"><see langword="true"/> if the route to parse
|
||||
/// is a base route; otherwise, <see langword="false"/>.</param>
|
||||
/// <returns>A newly-constructed instance of <see cref="RouteMatcher"/>
|
||||
/// that will match URL paths against <paramref name="route"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="route"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="FormatException"><paramref name="route"/> is not a valid route.</exception>
|
||||
/// <seealso cref="TryParse"/>
|
||||
/// <seealso cref="ClearCache"/>
|
||||
public static RouteMatcher Parse(string route, bool isBaseRoute)
|
||||
{
|
||||
var exception = TryParseInternal(route, isBaseRoute, out var result);
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
return result!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Attempts to obtain an instance of <see cref="RouteMatcher" /> by parsing the specified route.</para>
|
||||
/// <para>If the same route was previously parsed and the <see cref="ClearCache"/> method has not been called since,
|
||||
/// this method obtains an instance from a static cache.</para>
|
||||
/// </summary>
|
||||
/// <param name="route">The route to parse.</param>
|
||||
/// <param name="isBaseRoute"><see langword="true"/> if the route to parse
|
||||
/// is a base route; otherwise, <see langword="false"/>.</param>
|
||||
/// <param name="result">When this method returns <see langword="true"/>, a newly-constructed instance of <see cref="RouteMatcher" />
|
||||
/// that will match URL paths against <paramref name="route"/>; otherwise, <see langword="null"/>.
|
||||
/// This parameter is passed uninitialized.</param>
|
||||
/// <returns><see langword="true"/> if parsing was successful; otherwise, <see langword="false"/>.</returns>
|
||||
/// <seealso cref="Parse"/>
|
||||
/// <seealso cref="ClearCache"/>
|
||||
public static bool TryParse(string route, bool isBaseRoute, out RouteMatcher? result)
|
||||
=> TryParseInternal(route, isBaseRoute, out result) == null;
|
||||
|
||||
/// <summary>
|
||||
/// Clears <see cref="RouteMatcher"/>'s internal instance cache.
|
||||
/// </summary>
|
||||
/// <seealso cref="Parse"/>
|
||||
/// <seealso cref="TryParse"/>
|
||||
public static void ClearCache()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
Cache.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this instance, suitable for use in hashing algorithms
|
||||
/// and data structures like a hash table.</returns>
|
||||
public override int GetHashCode() => CompositeHashCode.Using(Route, IsBaseRoute);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="object"/> is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <see cref="object"/> to compare with this instance.</param>
|
||||
/// <returns><see langword="true"/> if <paramref name="obj"/> is equal to this instance;
|
||||
/// otherwise, <see langword="false"/>.</returns>
|
||||
public override bool Equals(object? obj) => obj is RouteMatcher other && Equals(other);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this instance is equal to another instance of <see cref="RouteMatcher"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">A <see cref="RouteMatcher"/> to compare with this instance.</param>
|
||||
/// <returns><see langword="true"/> if this instance is equal to <paramref name="other"/>;
|
||||
/// otherwise, <see langword="false"/>.</returns>
|
||||
public bool Equals(RouteMatcher? other)
|
||||
=> other != null
|
||||
&& other.Route == Route
|
||||
&& other.IsBaseRoute == IsBaseRoute;
|
||||
|
||||
/// <summary>
|
||||
/// Matches the specified URL path against <see cref="Route"/>
|
||||
/// and extracts values for the route's parameters.
|
||||
/// </summary>
|
||||
/// <param name="path">The URL path to match.</param>
|
||||
/// <returns>If the match is successful, a <see cref="RouteMatch"/> object;
|
||||
/// otherwise, <see langword="null"/>.</returns>
|
||||
public RouteMatch? Match(string path)
|
||||
{
|
||||
if (path == null)
|
||||
return null;
|
||||
|
||||
// Optimize for parameterless base routes
|
||||
if (IsBaseRoute)
|
||||
{
|
||||
if (Route.Length == 1)
|
||||
return RouteMatch.UnsafeFromRoot(path);
|
||||
|
||||
if (ParameterNames.Count == 0)
|
||||
return RouteMatch.UnsafeFromBasePath(Route, path);
|
||||
}
|
||||
|
||||
var match = _regex.Match(path);
|
||||
if (!match.Success)
|
||||
return null;
|
||||
|
||||
return new RouteMatch(
|
||||
path,
|
||||
ParameterNames,
|
||||
match.Groups.Cast<Group>().Skip(1).Select(g => WebUtility.UrlDecode(g.Value)).ToArray(),
|
||||
IsBaseRoute ? "/" + path.Substring(match.Groups[0].Length) : null);
|
||||
}
|
||||
|
||||
private static Exception? TryParseInternal(string route, bool isBaseRoute, out RouteMatcher? result)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
string? pattern = null;
|
||||
var parameterNames = new List<string>();
|
||||
var exception = Routing.Route.ParseInternal(route, isBaseRoute, (_, n, p) => {
|
||||
parameterNames.AddRange(n);
|
||||
pattern = p;
|
||||
});
|
||||
if (exception != null)
|
||||
{
|
||||
result = null;
|
||||
return exception;
|
||||
}
|
||||
|
||||
route = UrlPath.UnsafeNormalize(route, isBaseRoute);
|
||||
if (Cache.TryGetValue((isBaseRoute, route), out result))
|
||||
return null;
|
||||
|
||||
result = new RouteMatcher(isBaseRoute, route, pattern!, parameterNames);
|
||||
Cache.Add((isBaseRoute, route), result);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user