NAME

Langertha::Raider::Detect - Internal evaluator for declarative pack detection rules

VERSION

version 0.503

SYNOPSIS

# Internal to Langertha-Raider -- no API promise.
my $detect = Langertha::Raider::Detect->new( root => $workspace );

Langertha::Raider::Detect->validate_rule($rule, 'perl');   # croaks when invalid

my $result = $detect->evaluate({
  must     => [ { file => 'cpanfile' } ],
  may      => [ { file => 'dist.ini' }, { file => 'lib/**/*.pm' } ],
  must_not => [ { file => '.raider/no-perl' } ],
}, 'perl');

print $result->{matched} ? 'perl: '.$result->{reason} : 'no perl';

DESCRIPTION

Internal module. Its interface may change without notice.

Evaluates one detection rule of ADR 0012 against a workspace root. A rule is a map of up to three clause lists:

must -- every condition holds.
may -- when the list is non-empty, at least one condition holds.
must_not -- no condition holds.

A rule without any condition never matches. The clauses are evaluated in the order must, must_not, may, and evaluation stops as soon as the outcome is decided.

A condition is a map; every key in it must hold:

file: GLOB -- a file matching the glob exists.
dir: GLOB -- a directory matching the glob exists.
contains: STRING -- with file, a matching file contains the literal string.
matches: REGEX -- with file, a matching file matches the regex (multi-line: ^ and $ anchor at line ends). With contains as well, both must hold in the same file.

Globs are relative to the workspace root and know * and ? (within one path segment) and ** (any number of directories, including none; a trailing ** matches everything below). Wildcards do not match names that start with a dot unless the pattern segment does, so ** never walks .git. .. and absolute globs are invalid.

Evaluation is bounded: symlinks are followed only when they resolve inside the root, and ** never descends into a symlinked directory; a ** descends at most "max_depth" directories; one condition looks at no more than "max_entries" directory entries; content checks read at most the first "max_bytes" of a file. Hitting a limit makes the condition false and is reported in notes, it never stops the run. Content is matched as UTF-8 text when it decodes, as bytes otherwise.

root

The workspace root the globs are relative to. Required.

max_depth

How many directories one ** descends at most. Defaults to 8.

max_entries

How many directory entries one condition looks at before it gives up. Defaults to 5000.

max_bytes

How many bytes of a file a content check reads. Defaults to 65536 (64 KiB).

validate_rule

Langertha::Raider::Detect->validate_rule($rule, $label);

Croaks with Invalid detect rule LABEL...: problem when $rule is not a valid rule: not a map, an unknown clause or condition key, a clause that is not a list, a condition without file or dir, contains or matches without file, an empty, absolute or .. glob, or a regex that does not compile. $label (e.g. the pack name) prefixes the location; it defaults to rule. Returns true. Callable on the class.

describe_condition

my $text = Langertha::Raider::Detect->describe_condition({ file => 'dist.ini', contains => 'GETTY' });
# 'file=dist.ini contains="GETTY"'

One-line form of a condition, as used in reason and checks. Callable on the class.

evaluate

my $result = $detect->evaluate($rule, $label);

Validates the rule (see "validate_rule") and evaluates it against "root":

{
  matched => 1,
  reason  => 'must file=cpanfile (cpanfile); may file=dist.ini (dist.ini)',
  checks  => [
    { clause => 'must', condition => 'file=cpanfile', held => 1, path => 'cpanfile' },
    { clause => 'may',  condition => 'file=dist.ini', held => 1, path => 'dist.ini' },
  ],
  notes   => [],
}

reason names the conditions that decided the outcome -- what matched and where for a match, the condition that failed (no match, found PATH, none of ... held) otherwise; no clauses for an empty rule. checks lists every condition that was evaluated, notes every limit that was hit by a condition that did not hold.

SEE ALSO

SUPPORT

Issues

Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha-raider/issues.

IRC

Join #langertha on irc.perl.org or message Getty directly.

CONTRIBUTING

Contributions are welcome! Please fork the repository and submit a pull request.

AUTHOR

Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Torsten Raudssus.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.