lib/utils/ssh_tunnel_specification.rb



module Utils
  # A class that represents an SSH tunnel specification for configuring network
  # connections.
  #
  # This class parses and stores the configuration details for SSH tunnels,
  # including local and remote address/port combinations. It provides methods
  # to validate the specification, convert it to string or array
  # representations, and access individual components of the tunnel
  # configuration.
  #
  # @example
  #   spec = Utils::SshTunnelSpecification.new('localhost:8080:remote.host:22')
  #   spec.local_addr  # => 'localhost'
  #   spec.local_port  # => 8080
  #   spec.remote_addr # => 'remote.host'
  #   spec.remote_port # => 22
  class SshTunnelSpecification
    # Initializes a new SshTunnelSpecification instance by parsing the provided
    # specification string.
    #
    # This method takes a specification string and extracts local and remote
    # address/port combinations to configure the SSH tunnel parameters. The
    # specification can take various formats including port-only
    # specifications, localhost mappings, and full address:port combinations.
    #
    # @param spec_string [ String ] the specification string defining the SSH
    # tunnel configuration
    def initialize(spec_string)
      interpret_spec(spec_string)
    end

    # Returns the local address component of the SSH tunnel specification.
    #
    # @return [ String, nil ] the local address used for the SSH tunnel connection
    attr_reader :local_addr

    # Returns the local port component of the SSH tunnel specification.
    #
    # @return [ Integer, nil ] the local port number used for the SSH tunnel connection
    attr_reader :local_port

    # Returns the remote address component of the SSH tunnel specification.
    #
    # @return [ String, nil ] the remote address used for the SSH tunnel connection
    attr_reader :remote_addr

    # Returns the remote port component of the SSH tunnel specification.
    #
    # @return [ Integer, nil ] the remote port number used for the SSH tunnel connection
    attr_reader :remote_port

    # Returns an array representation of the SSH tunnel specification.
    #
    # This method combines the local and remote address/port components into a
    # four-element array in the order: [local_addr, local_port, remote_addr, remote_port].
    #
    # @return [ Array<String, Integer, String, Integer> ] an array containing the
    #         local address, local port, remote address, and remote port values
    def to_a
      [ local_addr, local_port, remote_addr, remote_port ]
    end

    # Checks if all components of the SSH tunnel specification are present and
    # valid.
    #
    # This method verifies that all address and port components of the tunnel
    # configuration have been set. If all components are present, it returns
    # the string representation of the specification; otherwise, it returns
    # nil.
    #
    # @return [ String, nil ] the string representation of the specification if all
    #         components are present, otherwise nil
    def valid?
      if to_a.all?
        to_s
      end
    end

    # Returns a string representation of the SSH tunnel specification.
    #
    # This method combines the local address, local port, remote address, and
    # remote port components into a single colon-separated string format.
    #
    # @return [ String ] a colon-separated string containing the tunnel specification
    #         in the format "local_addr:local_port:remote_addr:remote_port"
    def to_s
      to_a * ':'
    end

    private

    # Parses a specification string to extract local and remote address/port
    # components for SSH tunnel configuration.
    #
    # This method processes a given specification string and extracts the
    # necessary components to configure an SSH tunnel, handling various format
    # patterns including port-only specifications, localhost mappings, and full
    # address:port combinations.
    #
    # @param spec_string [ String ] the specification string defining the SSH tunnel configuration
    #
    # @return [ Array<String, Integer, String, Integer> ] an array containing the local address,
    #         local port, remote address, and remote port values extracted from the specification
    def interpret_spec(spec_string)
      @local_addr, @local_port, @remote_addr, @remote_port =
        case spec_string
        when /\A(\d+)\z/
          [ 'localhost', $1.to_i, 'localhost', $1.to_i ]
        when /\A(\[[^\]]+\]|[^:]+):(\d+)\z/
          [ 'localhost', $2.to_i, $1, $2.to_i ]
        when /\A(\d+):(\[[^\]]+\]|[^:]+):(\d+)\z/
          [ 'localhost', $1.to_i, $2, $3.to_i ]
        when /\A(\[[^\]]+\]|[^:]+):(\[[^\]]+\]|[^:]+):(\d+)\z/
          [ $1, $3.to_i, $2, $3.to_i ]
        when /\A(\[[^\]]+\]|[^:]+):(\d+):(\[[^\]]+\]|[^:]+):(\d+)\z/
          [ $1, $2.to_i, $3, $4.to_i ]
        end
    end
  end
end