File Coverage

blib/lib/YAMLScript.pm
Criterion Covered Total %
statement 34 37 91.8
branch 7 12 58.3
condition 2 3 66.6
subroutine 8 8 100.0
pod 1 2 50.0
total 52 62 83.8


line stmt bran cond sub pod time code
1             # Copyright 2023-2025 Ingy dot Net
2             # This code is licensed under MIT license (See License for details)
3              
4 1     1   237001 use strict;
  1         2  
  1         44  
5 1     1   12 use warnings;
  1         2  
  1         61  
6              
7             package YAMLScript;
8              
9 1     1   794 use FFI::CheckLib ();
  1         10271  
  1         38  
10 1     1   1167 use FFI::Platypus;
  1         11334  
  1         41  
11 1     1   1344 use Cpanel::JSON::XS ();
  1         5538  
  1         857  
12              
13             our $VERSION = '0.2.8';
14              
15             our $libys_version = $VERSION;
16              
17              
18             #------------------------------------------------------------------------------
19             # libys FFI setup:
20             #------------------------------------------------------------------------------
21              
22             # Find the proper libys version:
23             my $libys = find_libys();
24              
25             # Set up FFI functions:
26             my $ffi = FFI::Platypus->new(
27             api => 2,
28             lib => $libys,
29             );
30              
31             my $graal_create_isolate = $ffi->function(
32             graal_create_isolate =>
33             ['opaque', 'opaque*', 'opaque*'] => 'int',
34             );
35              
36             my $graal_tear_down_isolate = $ffi->function(
37             graal_tear_down_isolate =>
38             ['opaque'] => 'int',
39             );
40              
41              
42             #------------------------------------------------------------------------------
43             # YAMLScript object constructor and destructor:
44             #------------------------------------------------------------------------------
45              
46             # YAMLScript object constuctor. Creates and saves a graal isolate thread:
47             sub new {
48 1     1 1 249275 my ($class, $config) = (@_, {});
49 1         3 my ($isolatethread);
50 1 50       57 $graal_create_isolate->(undef, undef, \$isolatethread) == 0
51             or die 'Failed to create graal isolate';
52 1         17939 bless {
53             isolatethread => \$isolatethread,
54             }, $class;
55             }
56              
57             # Tear down the graal isolate when the YAMLScript object goes out of scope:
58             sub DESTROY {
59 1     1   3 my ($self) = @_;
60 1 50       2 $graal_tear_down_isolate->(${$self->{isolatethread}}) == 0
  1         23  
61             or die "Failed to tear down graal isolate";
62             }
63              
64             #------------------------------------------------------------------------------
65             # YAMLScript API methods:
66             #------------------------------------------------------------------------------
67             sub load;
68             # "load" method wrapper for FFI.
69             # It calls the libys load_ys_to_json function.
70             $ffi->attach(
71             [load_ys_to_json => 'load'] =>
72             ['sint64', 'string'] => 'string' =>
73             sub {
74             my ($xsub, $self, $ys) = @_;
75             $self->{error} = undef;
76              
77             my $resp = Cpanel::JSON::XS::decode_json(
78             $xsub->(${$self->{isolatethread}}, $ys)
79             );
80              
81             return $resp->{data} if exists $resp->{data};
82              
83             if ($self->{error} = $resp->{error}) {
84             die "libys: $self->{error}{cause}";
85             }
86              
87             die "Unexpected response from 'libys'";
88             },
89             );
90              
91             #------------------------------------------------------------------------------
92             # Helper functions:
93             #------------------------------------------------------------------------------
94             # Look for the local libys first, then look for the Alien version:
95             sub find_libys {
96 1     1 0 2 my $vers = $libys_version;
97 1 50       9 my $so = $^O eq 'darwin' ? 'dylib' : 'so';
98 1         3 my $name = "libys.$so.$vers";
99 1         2 my @paths;
100 1 50       6 if (my $path = $ENV{LD_LIBRARY_PATH}) {
101 0         0 @paths = split /:/, $path;
102             }
103 1         7 push @paths, qw(
104             /usr/local/lib
105             /usr/local/lib64
106             /usr/lib
107             /usr/lib64
108             ), "$ENV{HOME}/.local/lib";
109 1         2 for my $path (@paths) {
110 5 50       528 if (-e "$path/$name") {
111 0         0 return "$path/$name";
112             }
113             }
114              
115 1         671 require Alien::YAMLScript;
116              
117 1         105549 for my $path (Alien::YAMLScript->dynamic_libs) {
118 3 100 66     3664 if ($path =~ /\Q$name\E$/ && -r $path) {
119 1         8 return $path;
120             }
121             }
122              
123             die <<"..."
124             Shared library file $name not found
125             Try: curl https://yamlscript.org/install | VERSION=$vers LIB=1 bash
126             See: https://github.com/yaml/yamlscript/wiki/Installing-YAMLScript
127             ...
128 0           }
129              
130             1;