<commit_before>use v6;
unit module Terminal::Readsecret;

use NativeCall;

my constant $library = %?RESOURCES<libraries/readsecret>.Str;

my Int enum rsecret_error_ty (
    RSECRET_SUCCESS => 0,
    RSECRET_ERROR_BAD_ARG => 1, # arguments to function are invalid 
    RSECRET_ERROR_TTY_OPEN => 2, #  can not open the controlling terminal 
    RSECRET_ERROR_SIGACTION => 3, # failed to establish signal handlers
    RSECRET_ERROR_NOECHO => 4, # failed to set terminal to no-echo mode
    RSECRET_ERROR_PROMPT => 5, # failed to write prompt
    RSECRET_ERROR_READ => 6, # failure during reading of user input
    RSECRET_ERROR_LENGTH => 7, # user input was too long to hold in the supplied buffer
    RSECRET_ERROR_INTERRUPTED => 8, # program was interrupted during input
    RSECRET_ERROR_TIMER_CREATE => 9, # could not create real-time timer
    RSECRET_ERROR_TIMEOUT => 10 # User not quick enough, a timeout value expired
);

my constant max-secret-length = 1024;
my constant time_t = int64;

class timespec is repr('CStruct') is export {
    has time_t $.tv_sec;
    has int64 $.tv_nsec;
}

my sub rsecret_get_secret_from_tty(CArray[int8], size_t, Str) returns int32 is native($library) is export { * }
my sub rsecret_get_secret_from_tty_timed(CArray[int8], size_t, Str, timespec) returns int32 is native($library) is export { * }
my sub rsecret_strerror(int32) returns Str is native($library) is export { * }

proto getsecret(Str:D $msg, |) { * }

multi sub getsecret(Str:D $msg, timespec $timeout) returns Str is export {
    my $buf = CArray[int8].new;
    $buf[max-secret-length] = 0;
    my size_t $size = nativesizeof(int8) * max-secret-length;
    my $status = rsecret_get_secret_from_tty_timed($buf, $size, $msg, $timeout);
    validate-response($status);
    buf2secret($buf);
}

multi sub getsecret(Str:D $msg) returns Str is export {
    my $buf = CArray[int8].new;
    $buf[max-secret-length] = 0;
    my size_t $size = nativesizeof(int8) * max-secret-length;
    my $status = rsecret_get_secret_from_tty($buf, $size, $msg);
    validate-response($status);
    buf2secret($buf);
}

my sub validate-response(Int $status) returns Bool {
    if ($status != RSECRET_SUCCESS) {
	die rsecret_strerror($status);
    }
    return True;
}

my sub buf2secret(CArray[int8] $buf) {
    my Str $secret = "";
    loop (my $i = 0; $i < max-secret-length; $i++) {
	last if Int.new($buf[$i]) == 0;
	$secret = $secret ~ Int.new($buf[$i]).chr;
    }
    return $secret;
}

=begin pod

=head1 NAME

Terminal::Readsecret - A perl6 binding of readsecret ( https://github.com/dmeranda/readsecret ) for reading secrets or passwords from a command line secretly (not being displayed)

=head1 SYNOPSIS

=head2 EXAMPLE1<commit_msg>Call nativecast instead of buf2secret<commit_after>use v6;
unit module Terminal::Readsecret;

use NativeCall;

my constant $library = %?RESOURCES<libraries/readsecret>.Str;

my Int enum rsecret_error_ty (
    RSECRET_SUCCESS => 0,
    RSECRET_ERROR_BAD_ARG => 1, # arguments to function are invalid 
    RSECRET_ERROR_TTY_OPEN => 2, #  can not open the controlling terminal 
    RSECRET_ERROR_SIGACTION => 3, # failed to establish signal handlers
    RSECRET_ERROR_NOECHO => 4, # failed to set terminal to no-echo mode
    RSECRET_ERROR_PROMPT => 5, # failed to write prompt
    RSECRET_ERROR_READ => 6, # failure during reading of user input
    RSECRET_ERROR_LENGTH => 7, # user input was too long to hold in the supplied buffer
    RSECRET_ERROR_INTERRUPTED => 8, # program was interrupted during input
    RSECRET_ERROR_TIMER_CREATE => 9, # could not create real-time timer
    RSECRET_ERROR_TIMEOUT => 10 # User not quick enough, a timeout value expired
);

my constant max-secret-length = 1024;
my constant time_t = int64;

class timespec is repr('CStruct') is export {
    has time_t $.tv_sec;
    has int64 $.tv_nsec;
}

my sub rsecret_get_secret_from_tty(CArray[uint8], size_t, Str) returns int32 is native($library) is export { * }
my sub rsecret_get_secret_from_tty_timed(CArray[uint8], size_t, Str, timespec) returns int32 is native($library) is export { * }
my sub rsecret_strerror(int32) returns Str is native($library) is export { * }

proto getsecret(Str:D $msg, |) { * }

multi sub getsecret(Str:D $msg, timespec $timeout) returns Str is export {
    my $buf = CArray[uint8].new;
    $buf[max-secret-length] = 0;
    my size_t $size = nativesizeof(uint8) * max-secret-length;
    my $status = rsecret_get_secret_from_tty_timed($buf, $size, $msg, $timeout);
    validate-response($status);
    nativecast(Str, $buf)
}

multi sub getsecret(Str:D $msg) returns Str is export {
    my $buf = CArray[uint8].new;
    $buf[max-secret-length] = 0;
    my size_t $size = nativesizeof(uint8) * max-secret-length;
    my $status = rsecret_get_secret_from_tty($buf, $size, $msg);
    validate-response($status);
    nativecast(Str, $buf)
}

my sub validate-response(Int $status) returns Bool {
    if ($status != RSECRET_SUCCESS) {
        die rsecret_strerror($status);
    }
    return True;
}

=begin pod

=head1 NAME

Terminal::Readsecret - A perl6 binding of readsecret ( https://github.com/dmeranda/readsecret ) for reading secrets or passwords from a command line secretly (not being displayed)

=head1 SYNOPSIS

=head2 EXAMPLE1
{'fraction_non_alphanumeric': 0.10726511981742107, 'fraction_numerical': 0.016546215290985166, 'mean_word_length': 4.217261904761905, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 27, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4482980', 'n_tokens_mistral': 1865, 'n_tokens_neox': 1778, 'n_words': 524}