File Coverage

blib/lib/Text/Hunspell/FFI.pm
Criterion Covered Total %
statement 30 30 100.0
branch 6 10 60.0
condition n/a
subroutine 9 9 100.0
pod n/a
total 45 49 91.8


line stmt bran cond sub pod time code
1             package Text::Hunspell::FFI;
2              
3 10     10   1014399 use strict;
  10         94  
  10         297  
4 10     10   50 use warnings;
  10         18  
  10         240  
5 10     10   248 use 5.020;
  10         31  
6 10     10   6394 use FFI::Platypus 1.00;
  10         78893  
  10         340  
7 10     10   4580 use FFI::CheckLib 0.27 ();
  10         24902  
  10         390  
8 10     10   5081 use experimental qw( postderef );
  10         31330  
  10         54  
9              
10             # ABSTRACT: Perl FFI interface to the Hunspell library
11             our $VERSION = '0.04'; # VERSION
12              
13             sub _lib
14             {
15 6677     6677   1118556 my @args = (lib => '*', verify => sub { $_[0] =~ /hunspell/ }, symbol => "Hunspell_create");
  11     11   70  
16 11         55 my @libs = FFI::CheckLib::find_lib @args;
17 11 50       6183 my($first) = @libs
18             ? @libs
19             : FFI::CheckLib::find_lib @args, alien => 'Alien::Hunspell';
20 11 50       328 die "unable to find libs" unless $first;
21 11         218 $first;
22             }
23              
24             my $ffi = FFI::Platypus->new(
25             api => 1,
26             lib => _lib(),
27             );
28              
29             $ffi->attach(['Hunspell_create'=>'new'] => ['string','string'] => 'opaque', sub
30             {
31             my($xsub, $class, $aff, $dic) = @_;
32             my $ptr = $xsub->($aff, $dic);
33             bless \$ptr, $class;
34             });
35              
36              
37             $ffi->attach(['Hunspell_destroy'=>'DESTROY'] => ['opaque'] => 'void', sub
38             {
39             my($xsub, $self) = @_;
40             $xsub->($$self);
41             });
42              
43             foreach my $try (qw( Hunspell_add_dic _ZN8Hunspell7add_dicEPKcS1_ ))
44             {
45             eval {
46             $ffi->attach([$try=>'add_dic'] => ['opaque','string'] => 'void', sub
47             {
48             my($xsub, $self, $dpath) = @_;
49             $xsub->($$self, $dpath);
50             });
51             };
52             last unless $@;
53             }
54              
55             unless(__PACKAGE__->can('add_dic'))
56             {
57             # TODO: fallback on Perl implementation ?
58             die "unable to find add_dic";
59             }
60              
61             $ffi->attach(['Hunspell_spell'=>'check'] => ['opaque','string'] => 'int', sub
62             {
63             my($xsub, $self, $word) = @_;
64             $xsub->($$self, $word);
65             });
66              
67             $ffi->attach(['Hunspell_free_list',=>'_free_list'] => ['opaque','opaque*','int'] => 'void');
68              
69             sub _string_array_and_word
70             {
71 2     2   585 my($xsub, $self, $word) = @_;
72 2         5 my $ptr;
73 2         231 my $count = $xsub->($$self, \$ptr, $word);
74 2 50       24 my @result = defined $ptr ? map { $ffi->cast('opaque','string',$_) } $ffi->cast('opaque',"opaque[$count]", $ptr)->@* : ();
  4         558  
75 2 50       188 _free_list($self, $ptr, $count) if defined $ptr;
76 2 100       11 wantarray ? @result : $result[0]; ## no critic (Freenode::Wantarray)
77             }
78              
79             $ffi->attach(['Hunspell_suggest'=>'suggest'] => ['opaque','opaque*','string'] => 'int', \&_string_array_and_word);
80             $ffi->attach(['Hunspell_analyze'=>'analyze'] => ['opaque','opaque*','string'] => 'int', \&_string_array_and_word);
81              
82             $ffi->attach(['Hunspell_generate'=>'generate'] => ['opaque','opaque*','string','string'] => 'int', sub {
83             my($xsub, $self, $word, $word2) = @_;
84             my $ptr;
85             my $count = $xsub->($$self, \$ptr, $word, $word2);
86             my @result = defined $ptr ? map { $ffi->cast('opaque','string',$_) } $ffi->cast('opaque',"opaque[$count]", $ptr)->@* : ();
87             _free_list($self, $ptr, $count) if $ptr;
88             wantarray ? @result : $result[0]; ## no critic (Freenode::Wantarray)
89             });
90              
91             $ffi->attach(['Hunspell_generate2'=>'generate2'] => ['opaque','opaque*','string','string[]','int'] => 'int', sub
92             {
93             my($xsub, $self, $word, $suggestions) = @_;
94             my $n = scalar @$suggestions;
95             my $ptr;
96             my $count = $xsub->($$self, \$ptr, $word, [@$suggestions], 1);
97             my @result = defined $ptr ? map { $ffi->cast('opaque','string',$_) } $ffi->cast('opaque',"opaque[$count]", $ptr)->@* : ();
98             _free_list($self, $ptr, $count) if defined $ptr;
99             wantarray ? @result : $result[0]; ## no critic (Freenode::Wantarray)
100             });
101              
102             1;
103              
104             __END__