At

 view release on metacpan or  search on metacpan

lib/At/Protocol/NSID.pm  view on Meta::CPAN


    #~ Human readable constraints on NSID:
    #~  - a valid domain in reversed notation
    #~  - followed by an additional period-separated name, which is camel-case letters
    sub ensureValidNsid($nsid) {

        # check that all chars are boring ASCII
        throw InvalidNsidError('Disallowed characters in NSID (ASCII letters, digits, dashes, periods only)') unless $nsid =~ /^[a-zA-Z0-9.-]*$/;
        #
        throw InvalidNsidError('NSID is too long (317 chars max)') if length $nsid > 253 + 1 + 63;
        my @labels = split /\./, $nsid, -1;    # negative length, ftw

        #
        throw InvalidNsidError('NSID needs at least three parts') if scalar @labels < 3;
        #
        for my $i ( 0 .. $#labels ) {
            my $l = $labels[$i];
            throw InvalidNsidError('NSID parts can not be empty') unless length $l;
            throw InvalidNsidError('NSID part too long (max 63 chars)')           if length $l > 63;
            throw InvalidNsidError('NSID parts can not start or end with hyphen') if $l =~ /^-|-$/;
            throw InvalidNsidError('NSID first part may not start with a digit')  if $i == 0        && $l =~ /^[0-9]/;
            throw InvalidNsidError('NSID name part must be letters or digits')    if $i == $#labels && $l !~ /^[a-zA-Z][a-zA-Z0-9]*$/;
        }
        1;
    }

    sub ensureValidNsidRegex ($nsid) {

        #~ simple regex to enforce most constraints via just regex and length.
        #~ hand wrote this regex based on above constraints
        throw InvalidNsidError(q[NSID didn't validate via regex])
            unless $nsid =~ /^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(\.[a-zA-Z][a-zA-Z0-9]{0,62})$/;
        throw InvalidNsidError('NSID is too long (317 chars max)') if length $nsid > 253 + 1 + 63;
        1;
    }
    #
    register 'InvalidNsidError', 1;
};
1;
__END__
=pod

=encoding utf-8

=head1 NAME

At::Protocol::NSID - AT Protocol NSID Validation

=head1 SYNOPSIS

    use At::Protocol::NSID qw[:all];
    try {
        ensureValidNSID( 'net.users.bob.ping' );
    }
    catch($err) {
        ...; # do something about it
    }

=head1 DESCRIPTION

Namespaced Identifiers (NSIDs) are used to reference Lexicon schemas for records, XRPC endpoints, and more.

The basic structure and semantics of an NSID are a fully-qualified hostname in Reverse Domain-Name Order, followed by a
simple name. The hostname part is the B<domain authority>, and the final segment is the B<name>.

This package aims to validate them.

=head1 Functions

You may import functions by name or with the C<:all> tag.

=head2 C<new( ... )>

Verifies an NSID and creates a new object containing it.

    my $nsid = At::Protocol::NSID->new( 'com.example.fooBar' );

On success, an object is returned that will stringify to the NSID itself.

=head2 C<parse( ... )>

    my $nsid = parse( 'com.example.fooBar' );

Wrapper around C<new(...)> for the sake of compatibility.

=head2 C<create( ... )>

    my $nsid = create( 'alex.example.com' );

Parses a 'normal' (sub)domain name and creates an NSID object.

=head2 C<authority( )>

    my $auth = $nsid->authority;

Returns the domain authority of the NSID.

=head2 C<name( )>

    my $name = $nsid->name;

Returns the name from the NSID.

=head2 C<isValid( ... )>

    my $ok = isValid( 'com.exa💩ple.thing' );

Returns a boolean value indicating whether the given NSID is valid and can be parsed.

=head2 C<ensureValidNsid( ... )>

    ensureValidNsid( '.com.example.wrong' );

Validates an NSID. Throws errors on failure and returns a true value on success.

=head2 C<ensureValidNsidRegex( ... )>

    ensureValidNsidRegex( 'com.example.right' );

Validates an NSID with cursory regex provided by the AT protocol designers. Throws errors on failure and returns a true
value on success.



( run in 0.596 second using v1.01-cache-2.11-cpan-5837b0d9d2c )