File Coverage

lib/Music/AirGuitar.pm
Criterion Covered Total %
statement 14 53 26.4
branch 0 24 0.0
condition 0 11 0.0
subroutine 5 9 55.5
pod 2 2 100.0
total 21 99 21.2


line stmt bran cond sub pod time code
1             # Copyright (c) 2025 Philipp Schafft
2              
3             # licensed under Artistic License 2.0 (see LICENSE file)
4              
5             # ABSTRACT: Interface for air guitars
6              
7              
8             package Music::AirGuitar;
9              
10 1     1   377099 use v5.16;
  1         4  
11 1     1   8 use strict;
  1         1  
  1         32  
12 1     1   5 use warnings;
  1         2  
  1         82  
13              
14 1     1   6 use Carp;
  1         3  
  1         192  
15              
16             our $VERSION = v0.04;
17              
18             my @_standard_types = qw(string Data::Identifier Data::TagDB::Tag Data::URIID::Base);
19              
20             my %_valid_attribute_types = (
21             displayname => [qw(string)],
22             player => \@_standard_types,
23             strings => [qw(uint)],
24             keys => [qw(uint)],
25             );
26              
27             my %_attribute_defaults = (
28             strings => 6,
29             keys => 0,
30             );
31              
32             {
33 1     1   19 no strict 'refs'; # This is an invalid access, but it is the only one working in perl v5.24.1, the correct one segfaults.
  1         3  
  1         881  
34             foreach my $attribute (keys %_valid_attribute_types) {
35 0     0     *$attribute = sub { my ($self, @args) = @_; return $self->_attribute($attribute, @args); };
  0            
36             }
37             }
38              
39              
40             sub new {
41 0     0 1   my ($pkg, %opts) = @_;
42 0           my $self = bless {}, $pkg;
43              
44 0           foreach my $attribute (keys %_valid_attribute_types) {
45 0 0         if (defined(my $value = delete $opts{$attribute})) {
46 0           my $found;
47              
48 0           foreach my $type (@{$_valid_attribute_types{$attribute}}) {
  0            
49 0 0         if ($type eq 'string') {
    0          
    0          
50 0           $found = !ref($value);
51             } elsif ($type eq 'uint') {
52 0   0       $found = !ref($value) && $value =~ /^[0-9]+$/;
53 0 0         $value = int($value) if $found;
54 0           } elsif (eval {$value->isa($type)}) {
55 0           $found = 1;
56             }
57              
58 0 0         last if $found;
59             }
60              
61 0 0         croak 'Type mismatch for attribute: '.$attribute unless $found;;
62              
63 0           $self->{$attribute} = $value;
64             }
65             }
66              
67 0 0         croak 'Invalid options present: '.join(', ', keys %opts) if scalar(keys %opts);
68              
69 0           foreach my $attribute (keys %_attribute_defaults) {
70 0   0       $self->{$attribute} //= $_attribute_defaults{$attribute};
71             }
72              
73 0 0 0       unless ($self->{strings} || $self->{keys}) {
74 0           croak 'Your guitar has no strings nor keys. Kinda dull!';
75             }
76              
77 0           return $self;
78             }
79              
80              
81             sub perform {
82 0     0 1   my ($self, %opts) = @_;
83 0   0       my $duration = $opts{duration} // '5:55';
84              
85 0 0         if ($duration =~ /:/) {
86 0           my $s = 0;
87              
88 0           foreach my $c (split /:/, $duration) {
89 0           $s *= 60;
90 0           $s += $c;
91             }
92              
93 0           $duration = $s;
94             } else {
95 0           $duration = int($duration);
96             }
97              
98 0           require Music::AirGuitar::Performance;
99              
100 0           return Music::AirGuitar::Performance->_new(guitar => $self, duration => $duration);
101             }
102              
103              
104             *be_most_excellent = *perform;
105              
106              
107              
108             # ---- Private helpers ----
109              
110             sub _attribute {
111 0     0     my ($self, $key, %opts) = @_;
112 0 0         return $self->{$key} if defined $self->{$key};
113 0 0         return $opts{default} if exists $opts{default};
114 0           croak 'No data for attribute: '.$key;
115             }
116              
117             1;
118              
119             __END__