class Protocol::HTTP::Body::File

A body which reads from a file.

def self.open(path, *arguments, **options)

@parameter path [String] the path to the file.

Open a file at the given path.
def self.open(path, *arguments, **options)
	self.new(::File.open(path, MODE), *arguments, **options)
end

def buffered

@returns [File] the duplicated body.

Returns a copy of the body, by duplicating the file descriptor, including the same range if specified.
def buffered
	self.class.new(@file.dup, @range, block_size: @block_size)
end

def close(error = nil)

@parameter error [Exception | Nil] the error that caused the file to be closed.

Close the file.
def close(error = nil)
	@file.close
	@remaining = 0
	
	super
end

def empty?

@returns [Boolean] whether more data should be read.
def empty?
	@remaining == 0
end

def initialize(file, range = nil, size: file.size, block_size: BLOCK_SIZE)

@parameter block_size [Integer] the block size to use when reading from the file.
@parameter size [Integer] the size of the file, if known.
@parameter range [Range] the range of bytes to read from the file.
@parameter file [::File] the file to read from.

Initialize the file body with the given file.
def initialize(file, range = nil, size: file.size, block_size: BLOCK_SIZE)
	@file = file
	@range = range
	
	@block_size = block_size
	
	if range
		@file.seek(range.min)
		@offset = range.min
		@length = @remaining = range.size
	else
		@file.seek(0)
		@offset = 0
		@length = @remaining = size
	end
end

def inspect

@returns [String] a string representation of the file body.

Inspect the file body.
def inspect
	"\#<#{self.class} file=#{@file.inspect} offset=#{@offset} remaining=#{@remaining}>"
end

def join

@returns [String] the remaining data.

Read all the remaining data from the file and return it as a single string.
def join
	return "" if @remaining == 0
	
	buffer = @file.read(@remaining)
	
	@remaining = 0
	
	return buffer
end

def read

@returns [String | Nil] the next chunk of data, or nil if the file is fully read.

Read the next chunk of data from the file.
def read
	if @remaining > 0
		amount = [@remaining, @block_size].min
		
		if chunk = @file.read(amount)
			@remaining -= chunk.bytesize
			
			return chunk
		end
	end
end

def ready?

@returns [Boolean] whether the body is ready to be read, always true for files.
def ready?
	true
end

def rewind

Rewind the file to the beginning of the range.
def rewind
	@file.seek(@offset)
	@remaining = @length
end

def rewindable?

@returns [Boolean] whether the body is rewindable, generally always true for seekable files.
def rewindable?
	true
end