API-MikroTik
view release on metacpan or search on metacpan
lib/API/MikroTik/Query.pm view on Meta::CPAN
package API::MikroTik::Query;
use Mojo::Base '-base';
use Exporter 'import';
use Scalar::Util 'blessed';
our @EXPORT_OK = ('build_query');
sub build_query {
my $query = blessed $_[0] ? $_[1] : $_[0];
return $$query if ref $query eq 'REF' && ref $$query eq 'ARRAY';
if (my $type = ref $query) {
return [_block(_ref_op($type), $query)];
}
else { return [] }
}
sub _block {
my ($logic, $items) = @_;
@{($items = [])} = map { $_ => $items->{$_} } sort keys %$items
if ref $items eq 'HASH';
my ($count, @words) = (0, ());
while (my $el = shift @$items) {
my @expr;
if (ref $el eq 'REF' && ref $$el eq 'ARRAY') {
@expr = @{$$el};
}
elsif (my $type = ref $el) {
@expr = _block(_ref_op($type), $el);
}
elsif ($el =~ /^-(?:and|or)$/) {
@expr = _block(_ref_op($el), shift @$items);
}
elsif ($el =~ /^-has(?:_not)?$/) {
push @words, '?' . ($el eq '-has_not' ? '-' : '') . (shift @$items);
$count++;
next;
}
else {
@expr = _value($el, shift @$items);
}
++$count && push @words, @expr if @expr;
}
push @words, '?#' . ($logic x ($count - 1)) if $count > 1;
return @words;
}
sub _ref_op {
return
($_[0] eq 'HASH' || $_[0] eq '-and') ? '&'
: ($_[0] eq 'ARRAY' || $_[0] eq '-or') ? '|'
: '';
}
sub _value {
my ($name, $val) = @_;
my $type = ref $val;
if ($type eq 'HASH') {
return _value_hash($name, $val);
}
elsif ($type eq 'ARRAY') {
return _value_array($name, '=', $val);
}
# SCALAR
return "?$name=" . ($val // '');
}
sub _value_array {
my ($name, $op, $block) = @_;
return () unless @$block;
my $logic = '|';
$logic = _ref_op(shift @$block)
if @$block[0] eq '-and' || @$block[0] eq '-or';
my ($count, @words) = (0, ());
for (@$block) {
my @expr
= ref $_ eq 'HASH'
? _value_hash($name, $_)
: _value_scalar($name, $op, $_);
++$count && push @words, @expr if @expr;
}
push @words, '?#' . ($logic x ($count - 1)) if $count > 1;
return @words;
}
sub _value_hash {
my ($name, $block) = @_;
my @words = ();
for my $op (sort keys %$block) {
my $val = $block->{$op};
return _value_array($name, $op, $val) if ref $val eq 'ARRAY';
push @words, _value_scalar($name, $op, $val);
}
my $count = keys %$block;
push @words, '?#' . ('&' x ($count - 1)) if $count > 1;
return @words;
}
sub _value_scalar {
my ($name, $op, $val) = (shift, shift, shift // '');
return ("?$name=$val", '?#!') if $op eq '-not';
return '?' . $name . $op . $val;
}
1;
=encoding utf8
=head1 NAME
API::MikroTik::Query - Build MikroTik queries from perl structures
=head1 SYNOPSIS
use API::MikroTik::Query qw(build_query);
# (a = 1 OR a = 2) AND (b = 3 OR c = 4 OR d = 5)
my $query = {
a => [1, 2],
[
b => 3,
c => 4,
d => 5
]
};
# Some bizarre nested expressions.
# (a = 1 OR b = 2 OR (e = 5 AND f = 6 AND g = 7))
# OR
# (c = 3 AND d = 4)
# OR
# (h = 8 AND i = 9)
$query = [
-or => {
a => 1,
b => 2,
-and => {e => 5, f => 6, g => 7}
},
# OR
-and => [
c => 3,
d => 4
],
# OR
{h => 8, i => 9}
];
=head1 DESCRIPTION
Simple and supposedly intuitive way to build MikroTik API queries. Following
ideas of L<SQL::Abstract>.
=head1 METHODS
( run in 0.510 second using v1.01-cache-2.11-cpan-39bf76dae61 )