App-GHGen
view release on metacpan or search on metacpan
lib/App/GHGen/Detector.pm view on Meta::CPAN
package App::GHGen::Detector;
use v5.36;
use strict;
use warnings;
use Path::Tiny;
use Exporter 'import';
our @EXPORT_OK = qw(
detect_project_type
get_project_indicators
);
our $VERSION = '0.06';
=head1 NAME
App::GHGen::Detector - Detect project type from repository contents
=head1 SYNOPSIS
use App::GHGen::Detector qw(detect_project_type);
my $type = detect_project_type();
# Returns: 'perl', 'node', 'python', etc.
=head1 FUNCTIONS
=head2 detect_project_type()
Detect the project type by examining indicator files in the current directory.
=head3 Purpose
Score the current working directory against ten known project types
(perl, node, python, rust, go, ruby, php, java, cpp, docker) and return
the highest-scoring type.
=head3 Arguments
None.
=head3 Returns
In scalar context: the type string (e.g. C<'perl'>) of the best match, or
C<undef> when no type scores above zero.
In list context: a list of C<{ type => Str, score => Int, indicators => [] }>
hash references sorted by descending score.
=head3 Side Effects
Reads the filesystem; results depend on the current working directory.
=head3 Usage Example
use App::GHGen::Detector qw(detect_project_type);
my $type = detect_project_type();
say $type // 'unknown';
=head3 API SPECIFICATION
=head4 Input
# No parameters.
=head4 Output (scalar context)
{ type => 'scalar' } # may be undef
=head4 Output (list context)
{ type => 'array', element => { type => 'hashref' } }
=head3 FORMAL SPECIFICATION
detect_project_type : â â¤* ⪠{ ⥠} | seq Detection
Detection â { type: ProjectType, score: â, indicators: seq â¤* }
detections â [ { type: t, score: score(t) } ⣠t â Types, score(t) > 0 ]
ranked â sort desc-score detections
scalar context: ranked = â
â ⥠| ranked â â
â ranked[0].type
list context: ranked
=cut
sub detect_project_type() {
my @detections;
# Check for each project type
for my $type (qw(perl node python rust go ruby php java cpp docker)) {
my $detector = "_detect_$type";
my $score = __PACKAGE__->can($detector)->();
push @detections, { type => $type, score => $score, indicators => [] } if $score > 0;
}
# Sort by score (highest first)
@detections = sort { $b->{score} <=> $a->{score} } @detections;
return undef unless @detections;
return wantarray ? @detections : $detections[0]->{type};
}
=head2 get_project_indicators($type)
Return the indicator file patterns associated with a given project type.
=head3 Purpose
Provide a human-readable or programmatic list of the files and patterns that
the detector uses to score each project type.
=head3 Arguments
=over 4
=item C<$type> (Str, optional)
A project type string such as C<'perl'>, C<'node'>, C<'python'>, etc.
When omitted or C<undef>, the full indicator table is returned.
=back
=head3 Returns
When C<$type> is given and known: an array reference of strings (file names /
glob patterns).
When C<$type> is C<undef> or omitted: a hash reference mapping every known
type to its indicator list.
When C<$type> is given but unknown: C<undef>.
=head3 Side Effects
None. Pure lookup function.
=head3 Usage Example
my $indicators = get_project_indicators('perl');
say for @$indicators;
my $all = get_project_indicators();
for my $type (sort keys %$all) {
say "$type: " . join(', ', @{$all->{$type}});
}
=head3 API SPECIFICATION
=head4 Input
{ type => { type => 'scalar', optional => 1 } }
=head4 Output
known type â { type => 'arrayref' }
all types â { type => 'hashref' }
unknown type â undef
=head3 FORMAL SPECIFICATION
get_project_indicators : â¤* ⪠{ ⥠} â seq â¤* ⪠(â¤* â seq â¤*) ⪠{ ⥠}
t given â§ t â KnownTypes â indicators[t] (ArrayRef)
t given â§ t â KnownTypes â ⥠(undef)
t absent â indicators (HashRef of all types)
=cut
sub get_project_indicators($type = undef) {
my %indicators = (
perl => [
'cpanfile', 'dist.ini', 'Makefile.PL', 'Build.PL',
'lib/*.pm', 't/*.t', 'META.json', 'META.yml'
],
node => [
'package.json', 'package-lock.json', 'yarn.lock',
'node_modules/', 'tsconfig.json', '.npmrc'
],
python => [
'requirements.txt', 'setup.py', 'pyproject.toml',
'Pipfile', 'poetry.lock', 'setup.cfg', 'tox.ini'
],
rust => [
'Cargo.toml', 'Cargo.lock', 'src/main.rs',
'src/lib.rs', 'rust-toolchain.toml'
],
go => [
'go.mod', 'go.sum', 'main.go', '*.go'
],
ruby => [
'Gemfile', 'Gemfile.lock', 'Rakefile',
'.ruby-version', 'config.ru'
],
php => [
'composer.json', 'composer.lock', 'phpunit.xml',
'phpunit.xml.dist', 'src/', 'tests/'
],
java => [
'pom.xml', 'build.gradle', 'build.gradle.kts',
'gradlew', 'mvnw', 'src/main/java/'
],
cpp => [
'CMakeLists.txt', 'Makefile', 'configure.ac',
'*.cpp', '*.hpp', '*.cc', '*.h'
( run in 0.610 second using v1.01-cache-2.11-cpan-9169edd2b0e )