### ----------------------------------------------------
### -- SSH::LibSSH
### -- Licenses: Artistic-2.0
### -- Authors: 
### -- File: lib/SSH/LibSSH.rakumod
### ----------------------------------------------------
### -- Chunk 2 of 2
 method close-stdin() {
            my $p = Promise.new;
            my $v = $p.vow;
            given get-event-loop() -> $loop {
                error-check($!session.session-handle, ssh_channel_send_eof($!channel-handle));
                $v.keep(True);
                CATCH {
                    default {
                        $v.break($_);
                    }
                }
            }
            await $p;
        }

        method exit() {
            my $p = Promise.new;
            my $v = $p.vow;
            given get-event-loop() -> $loop {
                $loop.run-on-loop: {
                    $loop.add-poller: -> $remove is rw {
                        if $!stdout-eof || $!stderr-eof {
                            my $exit = ssh_channel_get_exit_status($!channel-handle);
                            if $exit >= 0 {
                                $remove = True;
                                my @awaitees = ($!stdout-eof, $!stderr-eof).grep(*.defined);
                                if @awaitees {
                                    Promise.allof(@awaitees).then({ $v.keep($exit) });
                                }
                                else {
                                    $v.keep($exit);
                                }
                            }
                        }
                    }
                }
            }
            $p
        }

        method send_signal(Str $sig) {
            my $p = Promise.new;
            my $v = $p.vow;
            get-event-loop().run-on-loop: {

                with $!channel-handle {
                    error-check($!session.session-handle, ssh_channel_request_send_signal($_, $sig));
                }
                $v.keep(True);
                CATCH {
                    default {
                        $v.break($_);
                    }
                }
            }
            $p;
        }

        method close() {
            my $p = Promise.new;
            my $v = $p.vow;
            get-event-loop().run-on-loop: {
                with $!channel-handle {
                    error-check('close a channel', ssh_channel_close($_));
                    ssh_channel_free($_);
                }
                $!channel-handle = SSHChannel;
                $v.keep(True);
                CATCH {
                    default {
                        $v.break($_);
                    }
                }
            }
            await $p;
        }
    }

    # Wraps around Channel and provides an API more relevant to forwarding.
    class ForwardingChannel {
        has Channel $.channel handles <write print say close>;

        method Supply(*%options) {
            $!channel.stdout(|%options)
        }
    }

    method connect(Str :$host!, *%options --> Promise) {
        Session.new(:$host, |%options).connect
    }

	method list-auth-methods( Str :$host!, Str :$user, UInt :$port = 22, LogLevel :$log-level = None, UInt :$timeout = 10 --> Promise) {
        Session.new(:$host, :$port, :$log-level, :$timeout, :$user).list-auth-methods
	}

    class LogEntry {
        has LogLevel $.level;
        has Str $.function;
        has Str $.message;
    }

    my Supplier $logs;
    my Lock $logs-lock .= new;
    method logs(--> Supply) {
        $logs-lock.protect: {
            without $logs {
                $logs = Supplier.new;
                error-check('set logging callback', ssh_set_log_callback(
                    -> int32 $level-int, Str $function, Str $message, Pointer $ {
                        my $level = LogLevel($level-int);
                        $logs.emit(LogEntry.new(:$level, :$function, :$message));
                    }));
            }
            $logs.Supply
        }
    }
}

=begin pod

=head1 NAME

SSH::LibSSH - Asynchronous binding for libssh; client-only and limited functionality

=head1 SYNOPSIS

=begin code :lang<raku>

use SSH::LibSSH;

=end code

=head1 DESCRIPTION

An asynchronous Raku binding to LibSSH.  It only supports client
operations, and even then only some of those. It implements:

=item Connecting to SSH servers, performing server authentication and
client authentication (by default, using a running key agent or the
current user's private key; you can also provide a private key file
or a password)

=item Executing commands, sending stdin, reading stdout/stderr, and
getting the exit code

=item Port forwarding

=item Reverse port forwarding

=item Single file SCP in either direction

See the C<examples> directory for a set of examples to illustrate
usage of this module.

All operations are asynchronous, and the interface to the module
is expressed in terms of the Raku C<Promise> and C<Supply> types.

On Linux, install libssh with your package manager to use this
module.  On Windows, the installation of this module will download
a pre-built libssh.dll, so just install the module and you're good
to go.

Pull requests to add missing features, or better documentation,
are welcome.  Please file bug reports or feature requests using
GitHub Issues.

=head1 AUTHOR

Jonathan Worthington

=head1 COPYRIGHT AND LICENSE

Copyright 2017 - 2024 Jonathan Worthington

Copyright 2024 Raku Community

Copyright 2025 Denis Kanchev

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.

=end pod


