using Invercargill; using Invercargill.DataStructures; using Astralis; namespace Statum { /** * A successful match of a request URI against an entrypoint route. */ public class EntrypointMatch : Object { /** The entrypoint handler type bound to the matched route. */ public Type entrypoint_type { get; set; } /** Route parameters captured from the matched route. */ public ReadOnlyAssociative route_params { get; set; default = new Dictionary(); } } /** * Singleton table of `(route, entrypoint type)` pairs, populated by * {@link StatumConfigurator.add_page}. * * The {@link EntrypointEndpoint} matches the `uri` query parameter against * this table (using the same `{param}` segment matching as the Astralis * router) so that a page and its entrypoint agree on routing. */ public class EntrypointRouteTable : Object { private Series registrations = new Series(); /** Registers an entrypoint type for `route`. */ public void register(EndpointRoute route, Type entrypoint_type) { registrations.add(new EntrypointRegistration(route, entrypoint_type)); } /** * Matches `uri` against the registered routes, returning the bound * entrypoint type and any captured route parameters, or `null`. */ public EntrypointMatch? match(string uri) { var path_components = Wrap.array(uri.split("/")) .where(c => c.length != 0) .to_array(); foreach (var registration in registrations) { var route_segments = registration.route.route_segments.to_array(); var matched = try_match(path_components, route_segments); if (matched != null) { return new EntrypointMatch() { entrypoint_type = registration.entrypoint_type, route_params = (!)matched }; } } return null; } private static Dictionary? try_match(string[] path, string[] route_segments) { if (path.length != route_segments.length) { return null; } var params = new Dictionary(); for (var i = 0; i < path.length; i++) { var pattern = route_segments[i]; var value = path[i]; if (pattern.has_prefix("{") && pattern.has_suffix("}")) { params.set(pattern.substring(1, pattern.length - 2), value); } else if (pattern != value) { return null; } } return params; } private class EntrypointRegistration : Object { public EndpointRoute route { get; private set; } public Type entrypoint_type { get; private set; } public EntrypointRegistration(EndpointRoute route, Type entrypoint_type) { this.route = route; this.entrypoint_type = entrypoint_type; } } } }