# Copyright (c) 2025 Yuki Kimoto
# MIT License
class Mojolicious::Routes::Match {
version_from Mojolicious;
use Mojolicious::Routes::Route;
use Mojolicious::Routes::Match::StackData;
# Fields
has root : rw Mojolicious::Routes;
has endpoint : ro Mojolicious::Routes::Route;
has stack : rw Mojolicious::Routes::Match::StackData[];
has position : rw int;
# Undocumented Fields
has captures : Hash;
# Class Methods
static method new : Mojolicious::Routes::Match () {
my $self = new Mojolicious::Routes::Match;
$self->{stack} = new Mojolicious::Routes::Match::StackData[0];
return $self;
}
# Instance Methods
method find : void ($options : Hash) {
$self->_match($self->root, $options);
}
private method _match : int ($r : Mojolicious::Routes::Route, $options : Hash) {
my $path = copy $options->get("path")->(string);
my $path_save = copy $path;
$self->{captures} //= Hash->new;
my $captures_save = $self->{captures}->clone;
Fn->defer([$self : Mojolicious::Routes::Match, $options : Hash, $path_save : string, $captures_save : Hash] method : void () {
$options->{"path"} = $path_save;
$self->{captures} = $captures_save;
});
# Pattern
my $is_endpoint = $r->is_endpoint;
my $captures = $r->pattern->match_partial((mutable string)$path);
unless ($captures) {
return 0;
}
$options->set(path => $path);
for my $key (@{$captures->keys}) {
my $value = $captures->{$key};
$self->{captures}->{$key} = $value;
}
$captures = $self->{captures};
# Method
my $method_match = 0;
if ($r->methods) {
my $method = $options->get("method")->(string);
for my $_ (@{$r->methods}) {
if ($method eq $_) {
$method_match = 1;
last;
}
}
}
else {
$method_match = 1;
}
unless ($method_match) {
return 0;
}
# WebSocket
if ($r->is_websocket && !($options->{"websocket"} ? $options->{"websocket"}->(int) : 0)) {
return 0;
}
my $empty = !length $path || $path eq "/";
my $stack = (List of Mojolicious::Routes::Match::StackData)List->new_ref($self->stack);
# Endpoint (or intermediate destination)
if (($is_endpoint && $empty) || $r->inline) {
my $stack_data = Mojolicious::Routes::Match::StackData->new;
$stack_data->set_captures($captures->clone);
$stack_data->set_route($r);
$stack->push($stack_data);
if ($is_endpoint && $empty) {
$self->{endpoint} = $r;
return 1;
}
}
# Match children
my $snapshot = $r->parent ? (Mojolicious::Routes::Match::StackData[])$stack->to_array : new Mojolicious::Routes::Match::StackData[0];
for my $child (@{$r->children}) {
if ($self->_match($child, $options)) {
return 1;
}
$self->set_stack($snapshot);
}
}
}