The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

use strict;
our $VERSION = '0.002010';
use Carp qw/croak/;
my %EXPORT = (
looks_like_uuid => 1,
gen_uuid => 1,
GEN_UUID_BACKEND => 1,
uuid2bin => 1,
bin2uuid => 1,
);
sub import {
my $class = shift;
my $caller = caller;
my %gen_params;
my %import;
while (my $arg = shift @_) {
if ($EXPORT{$arg}) {
$import{$arg}++;
next;
}
if ($arg eq 'warn' || $arg eq 'backends') {
$gen_params{$arg} = shift @_;
next;
}
croak "Invalid argument '$arg'";
}
my $subs = $class->get_gen_uuid(%gen_params);
for my $name (keys %import) {
my $sub = $subs->{$name} || $class->can($name) or croak "'$name' is not available for import";
no strict 'refs';
*{"$caller\::$name"} = $sub;
}
return;
}
my %GEN_UUID_CACHE;
sub clear_cache { %GEN_UUID_CACHE = () }
sub get_gen_uuid {
my $class = shift;
my %params = @_;
my $warn = $params{warn} // ($ENV{TEST2_UUID_NO_WARN} ? 0 : 1);
my $backends = $params{backends} // ($ENV{TEST2_UUID_BACKEND} ? [split /\s*,\s*/, $ENV{TEST2_UUID_BACKEND}] : ['UUID', 'Data::UUID::MT', 'UUID::Tiny', 'Data::UUID']);
for my $backend (@$backends) {
return $GEN_UUID_CACHE{$backend} if $GEN_UUID_CACHE{$backend};
my $meth = lc("_gen_$backend");
$meth =~ s/::/_/g;
croak "'$backend' is not supported" unless $class->can($meth);
$GEN_UUID_CACHE{$backend} = $class->$meth($warn) or next;
$GEN_UUID_CACHE{$backend}->{GEN_UUID_BACKEND} = sub() { $backend };
return $GEN_UUID_CACHE{$backend};
}
croak "No UUID generator found, please install one of these: UUID, Data::UUID::MT, Data::UUID, or UUID::Tiny. ('UUID' is preferred over the others)\n";
}
sub _gen_uuid {
my $class = shift;
my ($warn) = @_;
local $@;
return undef unless eval { require UUID; 1 };
unless (eval { UUID->VERSION('0.35'); 1 }) {
warn "UUID version is too old, need 0.35 or greater to avoid a fork related bug. Please upgrade the UUID module.\n"
if $warn;
return;
}
return {
gen_uuid => sub { uc(UUID::uuid7->()) },
bin2uuid => sub { my $out; UUID::unparse($_[0], $out); uc($out) },
uuid2bin => sub { my $out; UUID::parse($_[0], $out); $out },
};
}
sub _gen_data_uuid_mt {
my $class = shift;
my ($warn) = @_;
local $@;
return undef unless eval { require Data::UUID::MT; 1 };
my $ug = Data::UUID::MT->new(version => 4);
my $out = {
gen_uuid => sub { uc($ug->create_string()) },
};
if (eval { require UUID::Tiny; 1 }) {
$out->{uuid2bin} = sub { UUID::Tiny::string_to_uuid($_[0]) },
$out->{bin2uuid} = sub { uc(UUID::Tiny::uuid_to_string($_[0])) },
}
return $out;
}
sub _gen_uuid_tiny {
my $class = shift;
my ($warn) = @_;
local $@;
return undef unless eval { require UUID::Tiny; 1 };
warn "Using UUID::Tiny for uuid generation. UUID::Tiny is significantly slower than the 'UUID' or 'Data::UUID::MT' modules, please install 'UUID' or 'Data::UUID::MT' if possible. If you insist on using UUID::Tiny you can set the TEST2_UUID_NO_WARN environment variable.\n"
if $warn;
return {
gen_uuid => sub { uc(UUID::Tiny::create_uuid_as_string(UUID::Tiny::UUID_V4())) },
bin2uuid => sub { uc(UUID::Tiny::uuid_to_string($_[0])) },
uuid2bin => sub { UUID::Tiny::string_to_uuid($_[0]) },
};
}
sub _gen_data_uuid {
my $class = shift;
my ($warn) = @_;
local $@;
return undef unless eval { require Data::UUID; 1 };
warn "Using Data::UUID to generate UUIDs, this works, but the UUIDs will not be suitible as database keys. Please install the 'UUID', 'Data::UUID::MT' or the slower but pure perl 'UUID::Tiny' cpan modules for better UUIDs. If you insist on using Data::UUID you can set the TEST2_UUID_NO_WARN environment variable.\n"
if $warn;
my ($UG, $UG_PID);
my $UG_INIT = sub {
return $UG if $UG && $UG_PID && $UG_PID == $$;
$UG_PID = $$;
return $UG = Data::UUID->new;
};
# Initialize it here in this PID to start
$UG_INIT->();
return {
gen_uuid => sub { uc($UG_INIT->()->create_str()) },
bin2uuid => sub { uc($UG_INIT->()->to_string($_[0])) },
uuid2bin => sub { $UG_INIT->()->from_string($_[0]) },
};
}
sub looks_like_uuid {
my ($in) = @_;
return $in if $in && $in =~ m/^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$/i;
return undef;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Test2::Util::UUID - Utils for generating UUIDs under Test2.
=head1 DESCRIPTION
This module provides a consistent UUID source for all of Test2.
=head1 SYNOPSIS
use Test2::Util::UUID qw/gen_uuid looks_like_uuid uuid2bin bin2uuid/;
my $uuid = gen_uuid;
my $bin = bin2uuid($uuid);
my $uuid_again = uuid2bin($uuid);
=head1 UNDER THE HOOD
One of the following modules will be used under the hood, they are listed here
in order of preference.
=over 4
=item L<UUID> >= 0.35
When possible this module will use the L<UUID> cpan module, but it must be
version 0.35 or greater to avoid a fork related bug. It will generate version 7
UUIDs as they are most suitible for database entry.
=item L<Data::UUID::MT>
L<Data::UUID::MT> is the second choice for UUID generation. With this module
version 4 UUIDs are generated as they are fairly usable in databases.
=item L<UUID::Tiny> - slow
L<UUID::Tiny> is used if the previous 2 are not available. This module is pure
perl and thus could be slower than the others. Version 4 UUIDs are generated
when this module is used.
A warning will be issued with this module. You can surpress the warning with
either the C<$TEST2_UUID_NO_WARN> environment variable or the C<< warn => 0 >>
import argument.
=item L<Data::UUID> - Not Suitible for Databases
This is the last resort module. This generates UUIDs fast, but they are of a
type/version that is not suitible for database keys.
A warning will be issued with this module. You can surpress the warning with
either the C<$TEST2_UUID_NO_WARN> environment variable or the C<< warn => 0 >>
import argument.
=back
=head2 CONTROLLING WARNINGS
=head3 AT IMPORT
use Test2::Util::UUID 'gen_uuid', warn => 0;
Passing in C<< warn => 0 >> will disable the warnings normally issued when
using L<UUID::Tiny> or L<Data::UUID> as a backend.
=head2 SELECTING A BACKEND
=head3 AT IMPORT
If you wish to override the order and specify which backend to use you may do
so by listing them during import prefixed with ':'.
use Test2::Util::UUID 'gen_uuid', backends => [':UUID', ':UUID::Tiny'];
The above will try the L<UUID> module first, followed by the L<UUID::Tiny>
module. It will not try L<Data::UUID::MT> or L<Data::UUID>.
B<Note:> You must include these import arguments anywhere you import this
module, otherwise the other imports will use the default list, resulting in
different places using different UUIDs.
=head3 THE $TEST2_UUID_BACKEND ENV VAR
Setting the $TEST2_UUID_BACKEND env var to one of the backends, or a comma
seperated list will override the default list for all imports. It will B<NOT>
override imports that specify their own lists.
=head1 EXPORTS
=over 4
=item $uuid = gen_uuid()
Generate a UUID, always normalized to upper case.
=item $uuid_or_undef = looks_like_uuid($UUID)
Checks if the provided value looks like a UUID. Make sure it is defined, 36
characters long, has dashes, and only contains 0-9 a-f and dash (case
insensitive).
Returns the input value if it looks like a UUID, otherise it returns undef.
=back
=head1 SOURCE
The source code repository for Test2-Plugin-UUID can be found at
=head1 MAINTAINERS
=over 4
=item Chad Granum E<lt>exodist@cpan.orgE<gt>
=back
=head1 AUTHORS
=over 4
=item Chad Granum E<lt>exodist@cpan.orgE<gt>
=back
=head1 COPYRIGHT
Copyright Chad Granum E<lt>exodist@cpan.orgE<gt>.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=cut