summaryrefslogtreecommitdiffstats
path: root/lib/Cepis.pm
diff options
context:
space:
mode:
authorMurilo Ijanc <[email protected]>2026-07-26 14:40:05 -0300
committerMurilo Ijanc <[email protected]>2026-07-26 14:40:05 -0300
commit0bc578d6d334c7f1ccaf329cd61bb8bce76b7f59 (patch)
tree86f73108f0432fd8330edcaab8b8ae7018322dee /lib/Cepis.pm
downloadperl-0.1.0.tar.gz
Initial importv0.1.0
Diffstat (limited to 'lib/Cepis.pm')
-rw-r--r--lib/Cepis.pm184
1 files changed, 184 insertions, 0 deletions
diff --git a/lib/Cepis.pm b/lib/Cepis.pm
new file mode 100644
index 0000000..224d80d
--- /dev/null
+++ b/lib/Cepis.pm
@@ -0,0 +1,184 @@
+package Cepis;
+
+use strict;
+use warnings;
+
+use Exporter 5.57 qw(import);
+
+use Cepis::Address;
+use Cepis::Client;
+use Cepis::Error;
+use Cepis::Geo;
+use Cepis::Health;
+
+our $VERSION = '0.1.0';
+
+our @EXPORT_OK = qw(lookup lookup_many health normalize_cep);
+
+my $DEFAULT_CLIENT;
+
+sub default_client {
+ $DEFAULT_CLIENT = Cepis::Client->new unless defined $DEFAULT_CLIENT;
+ return $DEFAULT_CLIENT;
+}
+
+sub lookup {
+ return default_client()->lookup(@_);
+}
+
+sub lookup_many {
+ return default_client()->lookup_many(@_);
+}
+
+sub health {
+ return default_client()->health;
+}
+
+sub normalize_cep {
+ return Cepis::Client::normalize_cep(@_);
+}
+
+1;
+
+__END__
+
+=encoding UTF-8
+
+=head1 NAME
+
+Cepis - Perl client for the public CEP API at cep.is
+
+=head1 SYNOPSIS
+
+ use Cepis;
+
+ my $address = Cepis::lookup('01001-000');
+ print $address->localidade, ' ', $address->uf, "\n"; # São Paulo SP
+
+ my $result = Cepis::lookup_many( [ '01001000', '99999999' ] );
+ print defined $result->[1] ? 'found' : 'not found', "\n";
+
+ my $health = Cepis::health();
+ print $health->dne, "\n";
+
+=head1 DESCRIPTION
+
+Looks Brazilian postal codes up against L<https://cep.is>: official Correios
+data (DNE), refreshed every two weeks. Single lookups carry coordinates when the
+database has them; batches resolve up to 500 CEPs in one request.
+
+B<No non-core dependencies.> L<HTTP::Tiny>, L<JSON::PP> and L<Encode> all ship
+with Perl, and the CSV reader is part of this distribution. Reaching
+C<https://cep.is> does need L<IO::Socket::SSL> and L<Net::SSLeay>, which do not
+ship with Perl — see L</"HTTPS support">.
+
+For a script, the module-level functions are enough. In an application, build a
+L<Cepis::Client> once and reuse it:
+
+ my $client = Cepis::Client->new( timeout => 5.0, total_timeout => 15.0 );
+ my $address = $client->lookup('01001000');
+
+Pointing at a local instance (the API repo brings one up with C<just up>):
+
+ my $client = Cepis::Client->new( base_url => 'http://localhost:8080' );
+
+=head2 Errors
+
+Every failure is thrown as an object under L<Cepis::Error>, so one test catches
+the lot, and the split below it is by whether retrying helps:
+
+ my $address = eval { Cepis::lookup($cep_from_form) };
+ if ( !$address ) {
+ my $err = $@;
+ if ( ref $err && $err->isa('Cepis::Error::CEPNotFound') ) {
+ # the CEP does not exist: ask the user for another
+ }
+ elsif ( ref $err && $err->isa('Cepis::Error') ) {
+ # network, server, rate limit: retry later
+ }
+ else {
+ die $err;
+ }
+ }
+
+=head2 Field names
+
+The API surface is English, but the B<data fields keep the API's own Portuguese
+names> — C<logradouro>, C<bairro>, C<localidade>. What you read in the JSON is
+what you read on the object. See L<Cepis::Address>.
+
+=head2 HTTPS support
+
+L<HTTP::Tiny> speaks HTTPS only when L<IO::Socket::SSL> 1.968 or later and
+L<Net::SSLeay> 1.49 or later are installed, and neither ships with Perl. Without
+them, any call against the default C<https://cep.is> throws
+L<Cepis::Error::Network> straight away with a message naming both modules.
+C<Cepis::Client::tls_available()> reports the same thing without making a
+request. See L<Cepis::Client/"HTTPS support">.
+
+=head1 FUNCTIONS
+
+The module-level functions all go through one shared default client, built on
+first use. It applies process-wide, including to third-party libraries that also
+use this module, so do not reconfigure it — anyone needing different settings
+should build their own L<Cepis::Client>.
+
+Nothing is exported by default. C<lookup>, C<lookup_many>, C<health> and
+C<normalize_cep> are available on request:
+
+ use Cepis qw(lookup lookup_many);
+
+=head2 lookup
+
+ my $address = Cepis::lookup('01001-000');
+
+Looks up a CEP using the default configuration and returns a
+L<Cepis::Address>. Shorthand for C<< Cepis::default_client->lookup($cep) >>; see
+L<Cepis::Client/lookup> for the errors it throws.
+
+=head2 lookup_many
+
+ my $result = Cepis::lookup_many( \@ceps );
+
+Looks many CEPs up in one batch, using the default configuration, and returns an
+arrayref of the same length and order with C<undef> for the misses. Shorthand
+for L<Cepis::Client/lookup_many> — read that one, especially the warning that
+batches carry no coordinate.
+
+=head2 health
+
+ my $health = Cepis::health();
+
+Queries the API's health using the default configuration and returns a
+L<Cepis::Health>. Shorthand for L<Cepis::Client/health>.
+
+=head2 normalize_cep
+
+ my $number = Cepis::normalize_cep('01001-000'); # "01001000"
+
+Strips the mask and validates that 8 digits remain, throwing
+L<Cepis::Error::InvalidCEP> otherwise. Makes no request. See
+L<Cepis::Client/normalize_cep>.
+
+=head2 default_client
+
+ my $client = Cepis::default_client();
+
+The L<Cepis::Client> behind the functions above, built on first use.
+
+=head1 SEE ALSO
+
+L<Cepis::Client>, L<Cepis::Address>, L<Cepis::Geo>, L<Cepis::Health>,
+L<Cepis::Error>
+
+The API itself is documented at L<https://cep.is>.
+
+=head1 AUTHOR
+
+Murilo Ijanc E<lt>[email protected]<gt>
+
+=head1 LICENCE
+
+ISC. Copyright (c) 2026 Murilo Ijanc E<lt>[email protected]<gt>.
+
+=cut