File Coverage

blib/lib/App/Fasops/Command/subset.pm
Criterion Covered Total %
statement 76 80 95.0
branch 19 26 73.0
condition 6 9 66.6
subroutine 12 12 100.0
pod 6 6 100.0
total 119 133 89.4


line stmt bran cond sub pod time code
1             package App::Fasops::Command::subset;
2 20     20   14659 use strict;
  20         53  
  20         650  
3 20     20   113 use warnings;
  20         44  
  20         622  
4 20     20   110 use autodie;
  20         48  
  20         124  
5              
6 20     20   105025 use App::Fasops -command;
  20         61  
  20         206  
7 20     20   6797 use App::RL::Common;
  20         49  
  20         547  
8 20     20   136 use App::Fasops::Common;
  20         44  
  20         18830  
9              
10             sub abstract {
11 2     2 1 48 return 'extract a subset of species from a blocked fasta';
12             }
13              
14             sub opt_spec {
15             return (
16 6     6 1 36 [ "outfile|o=s", "Output filename. [stdout] for screen" ],
17             [ "first", "Always keep the first species" ],
18             [ "required", "Skip blocks not containing all the names" ],
19             { show_defaults => 1, }
20             );
21             }
22              
23             sub usage_desc {
24 6     6 1 53653 return "fasops subset [options] ";
25             }
26              
27             sub description {
28 1     1 1 898 my $desc;
29 1         5 $desc .= ucfirst(abstract) . ".\n";
30 1         4 $desc .= <<'MARKDOWN';
31              
32             * is the path to blocked fasta file, .fas.gz is supported
33             * infile == stdin means reading from STDIN
34             * is a file with a list of names to keep, one per line
35             * Names in the output file will following the order in
36              
37             MARKDOWN
38              
39 1         3 return $desc;
40             }
41              
42             sub validate_args {
43 5     5 1 5100 my ( $self, $opt, $args ) = @_;
44              
45 5 100       10 if ( @{$args} != 2 ) {
  5         19  
46 2         4 my $message = "This command need two input files.\n\tIt found";
47 2         3 $message .= sprintf " [%s]", $_ for @{$args};
  2         8  
48 2         6 $message .= ".\n";
49 2         12 $self->usage_error($message);
50             }
51 3         7 for ( @{$args} ) {
  3         8  
52 5 50       136 next if lc $_ eq "stdin";
53 5 100       19 if ( !Path::Tiny::path($_)->is_file ) {
54 1         116 $self->usage_error("The input file [$_] doesn't exist.");
55             }
56             }
57              
58 2 50       106 if ( !exists $opt->{outfile} ) {
59 0         0 $opt->{outfile} = Path::Tiny::path( $args->[0] )->absolute . ".fas";
60             }
61             }
62              
63             sub execute {
64 2     2 1 14 my ( $self, $opt, $args ) = @_;
65              
66 2         4 my @names = @{ App::RL::Common::read_names( $args->[1] ) };
  2         9  
67              
68 2         6321 my $in_fh;
69 2 50       9 if ( lc $args->[0] eq "stdin" ) {
70 0         0 $in_fh = *STDIN{IO};
71             }
72             else {
73 2         9 $in_fh = IO::Zlib->new( $args->[0], "rb" );
74             }
75              
76 2         2709 my $out_fh;
77 2 50       8 if ( lc( $opt->{outfile} ) eq "stdout" ) {
78 2         7 $out_fh = *STDOUT{IO};
79             }
80             else {
81 0         0 open $out_fh, ">", $opt->{outfile};
82             }
83              
84             {
85 2         4 my $content = ''; # content of one block
  2         5  
86 2         3 BLOCK: while (1) {
87 56 100 66     360 last if $in_fh->eof and $content eq '';
88 54         2278 my $line = '';
89 54 50       164 if ( !$in_fh->eof ) {
90 54         2069 $line = $in_fh->getline;
91             }
92 54 100 66     6101 if ( ( $line eq '' or $line =~ /^\s+$/ ) and $content ne '' ) {
      66        
93 6         25 my $info_of = App::Fasops::Common::parse_block($content);
94 6         11 $content = '';
95              
96 6         16 my @needed_names = @names;
97 6 100       16 if ( $opt->{first} ) {
98 3         5 my $first = ( keys %{$info_of} )[0];
  3         11  
99 3         81 @needed_names = App::Fasops::Common::uniq( $first, @needed_names );
100             }
101              
102 6 100       18 if ( $opt->{required} ) {
103 3         6 for my $name (@needed_names) {
104 9 50       43 next BLOCK unless exists $info_of->{$name};
105             }
106             }
107              
108 6         21 for my $name (@needed_names) {
109 15 50       301 if ( exists $info_of->{$name} ) {
110 15         79 printf {$out_fh} ">%s\n",
111 15         63 App::RL::Common::encode_header( $info_of->{$name} );
112 15         2735 printf {$out_fh} "%s\n", $info_of->{$name}{seq};
  15         104  
113             }
114             }
115 6         178 print {$out_fh} "\n";
  6         22  
116             }
117             else {
118 48         98 $content .= $line;
119             }
120             }
121             }
122 2         353 close $out_fh;
123 0           $in_fh->close;
124             }
125              
126             1;