=encoding utf8

=head1 NAME

Sidef::Types::Glob::Pipe - Interface for creating and managing pipe handles

=head1 DESCRIPTION

The C<Pipe> class provides an interface for working with Unix-style pipes in Sidef. Pipes allow communication between your Sidef program and an external command: data can flow from a command into your program (read mode) or from your program into a command (write mode).

A Pipe object is constructed from one or more strings:

    Pipe("ls -la")        # a single command string
    Pipe("ls", "-la")     # a command followed by its arguments

This distinction matters: when the Pipe wraps a B<single> string, that string is handed to the shell for parsing (so shell metacharacters such as pipes, redirections and globs are interpreted). When the Pipe wraps B<multiple> strings, the first is executed directly as a program with the rest as its arguments, bypassing the shell entirely (safer, and immune to shell-injection issues).

The command isn't actually executed until C<open>, C<open_r>, or C<open_w> is called.

=head1 SYNOPSIS

    # Single string: parsed and run through the shell
    var pipe = Pipe("ps aux | grep sidef")
    pipe.open_r(\var fh)
    fh.each { |line|
        say line
    }
    fh.close

    # Multiple arguments: run directly, no shell involved
    var pipe = Pipe("grep", "pattern", "file.txt")
    pipe.open_r(\var fh)
    fh.each { |line| say line }
    fh.close

    # Create a pipe for writing to a command
    var pipe = Pipe("sort | uniq")
    pipe.open_w(\var fh)
    fh.say("banana")
    fh.say("apple")
    fh.say("banana")
    fh.close

    # Alternative: using open with an explicit mode
    var pipe = Pipe("wc -l")
    pipe.open('-|:utf8', \var in_fh)   # read mode
    # or
    pipe.open('|-:utf8', \var out_fh)  # write mode

=head1 INHERITS

Inherits methods from L<Sidef::Object::Object>.

=head1 CONSTRUCTION

=head2 new

    Pipe.new(command...)
    Pipe(command...)

Creates a new Pipe object wrapping the given command. Accepts either a single command string (which will later be parsed by the shell) or a program name followed by its individual arguments (which will later be executed directly, without a shell).

    var pipe1 = Pipe("grep 'error' /var/log/syslog")
    var pipe2 = Pipe("grep", "error", "/var/log/syslog")

The command is not executed until C<open>, C<open_r>, or C<open_w> is called.

Aliases: I<call>

=cut

=head1 COMMAND INFORMATION

=head2 command

    self.command

Returns the command wrapped by this Pipe object. If the Pipe was constructed from a single string, that string is returned. If it was constructed from multiple arguments, the full list of arguments is returned instead.

    var pipe = Pipe("ls -la")
    say pipe.command             # prints: ls -la

    var pipe2 = Pipe("ls", "-la", "/tmp")
    say pipe2.command            # prints: ls -la /tmp (as a list)

=cut

=head1 OPENING THE PIPE

=head2 open

    self.open(mode)
    self.open(mode, var_ref)

Opens the pipe using the given mode, executing the wrapped command as described in L</DESCRIPTION>. Valid modes are C<'-|'> for read (command output flows into your program) and C<'|-'> for write (your program's output flows into the command), optionally followed by a PerlIO layer such as C<':utf8'>.

If C<var_ref> is given, the resulting L<Sidef::Types::Glob::FileHandle> is stored into it, and C<open> returns the process ID of the spawned command (or C<nil> on failure). If C<var_ref> is omitted, C<open> returns the FileHandle directly instead (or C<nil> on failure).

    var pipe = Pipe("grep pattern file.txt")

    # capture the PID, get the filehandle via var_ref
    var pid = pipe.open('-|:utf8', \var fh)
    fh.each { |line| say line }
    fh.close

    # or get the filehandle directly, ignoring the PID
    var out_pipe = Pipe("sort")
    var fh = out_pipe.open('|-:utf8')
    fh.say("banana")
    fh.close

=cut

=head2 open_r

    self.open_r
    self.open_r(var_ref)

Opens the pipe in read mode (equivalent to C<open('-|:utf8', var_ref)>), allowing your program to read the command's output. As with C<open>, passing C<var_ref> makes this return the process ID (with the FileHandle stored into C<var_ref>); omitting it returns the FileHandle directly.

    var pipe = Pipe("ps aux | grep sidef")
    pipe.open_r(\var fh)

    fh.each { |line|
        say "Process: #{line}"
    }
    fh.close

Aliases: I<open_read>

=cut

=head2 open_w

    self.open_w
    self.open_w(var_ref)

Opens the pipe in write mode (equivalent to C<open('|-:utf8', var_ref)>), allowing your program to send data to the command's standard input. As with C<open>, passing C<var_ref> makes this return the process ID (with the FileHandle stored into C<var_ref>); omitting it returns the FileHandle directly.

    var pipe = Pipe("mail -s 'Report' user@example.com")
    pipe.open_w(\var fh)

    fh.say("This is the report content.")
    fh.close

Aliases: I<open_write>

=cut

=head1 STRING REPRESENTATION

=head2 dump

    self.dump

Returns a String representation of the Pipe object, showing each part of the wrapped command individually quoted, e.g. C<Pipe("grep", "pattern", "file.txt")>.

    var pipe = Pipe("echo", "hello")
    say pipe.dump             # prints: Pipe("echo", "hello")

Aliases: I<to_s>, I<to_str>

=cut

=head1 LOW-LEVEL ACCESS

=head2 get_value

    self.get_value

Returns the underlying command as a single space-joined string, regardless of how many arguments the Pipe was constructed with. This is a low-level accessor, mainly intended for internal use or interoperability rather than everyday scripting.

    var pipe = Pipe("ls", "-la", "/tmp")
    say pipe.get_value        # prints: ls -la /tmp

=cut

=head1 EXAMPLES

=head2 Reading from a Pipe

    # Execute a command and read its output line by line
    var pipe = Pipe("find /etc -name '*.conf'")
    pipe.open_r(\var fh)

    var count = 0
    fh.each { |line|
        say line.trim
        ++count
    }
    fh.close

    say "Found #{count} configuration files"

=head2 Writing to a Pipe

    # Send data to a command for processing
    var data = %w(zebra apple mango banana kiwi)
    var pipe = Pipe("sort -r")
    pipe.open_w(\var fh)

    data.each { |item|
        fh.say(item)
    }
    fh.close

=head2 Avoiding the Shell

    # Pass arguments separately to skip shell parsing entirely
    var pipe = Pipe("grep", "-n", "TODO", "main.sf")
    pipe.open_r(\var fh)

    fh.each { |line|
        say line
    }
    fh.close

=head2 Getting the Filehandle Without a Var Reference

    # open_r/open_w can return the filehandle directly
    var fh = Pipe("uptime").open_r
    say fh.read
    fh.close

=head2 Error Handling

    # Always check if pipe operations succeed
    var pipe = Pipe("some-command")

    try {
        pipe.open_r(\var fh)
        fh.each { |line| say line }
        fh.close
    } catch {
        say "Error: #{_}"
    }

=head1 SEE ALSO

L<Sidef::Types::Glob::File>, L<Sidef::Types::Glob::FileHandle>, L<Sidef::Types::Glob::Dir>, L<Sidef::Sys::Sys>

=head1 NOTES

Pipes should be closed when finished with, to avoid resource leaks and, for write pipes, to ensure buffered data is flushed to the command.

Whether shell metacharacters are interpreted depends entirely on how the Pipe was constructed: a single command string is parsed by the shell, while multiple arguments are executed directly without shell involvement.

=cut
