118 lines
2.3 KiB
Ruby
118 lines
2.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# Released under the MIT License.
|
|
# Copyright, 2020-2024, by Samuel Williams.
|
|
# Copyright, 2022, by Anton Sozontov.
|
|
|
|
require_relative "clock"
|
|
|
|
module Console
|
|
class Progress
|
|
def self.now
|
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
end
|
|
|
|
def initialize(subject, total = 0, minimum_output_duration: 0.1, **options)
|
|
@subject = subject
|
|
@options = options
|
|
|
|
@start_time = Progress.now
|
|
|
|
@last_output_time = nil
|
|
@minimum_output_duration = minimum_output_duration
|
|
|
|
@current = 0
|
|
@total = total
|
|
end
|
|
|
|
attr :subject
|
|
attr :current
|
|
attr :total
|
|
|
|
def duration
|
|
Progress.now - @start_time
|
|
end
|
|
|
|
def ratio
|
|
Rational(@current.to_f, @total.to_f)
|
|
end
|
|
|
|
def remaining
|
|
@total - @current
|
|
end
|
|
|
|
def average_duration
|
|
if @current > 0
|
|
duration / @current
|
|
end
|
|
end
|
|
|
|
def estimated_remaining_time
|
|
if average_duration = self.average_duration
|
|
average_duration * remaining
|
|
end
|
|
end
|
|
|
|
def to_hash
|
|
Hash.new.tap do |hash|
|
|
hash[:type] = :progress
|
|
hash[:current] = @current
|
|
hash[:total] = @total
|
|
|
|
hash[:duration] = self.duration
|
|
hash[:estimated_remaining_time] = self.estimated_remaining_time
|
|
end
|
|
end
|
|
|
|
def increment(amount = 1)
|
|
@current += amount
|
|
|
|
if output?
|
|
Console.call(@subject, self.to_s, event: self.to_hash, **@options)
|
|
@last_output_time = Progress.now
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
def resize(total)
|
|
@total = total
|
|
|
|
Console.call(@subject, self.to_s, event: self.to_hash, **@options)
|
|
@last_output_time = Progress.now
|
|
|
|
return self
|
|
end
|
|
|
|
def mark(*arguments, **options)
|
|
Console.call(@subject, *arguments, **options, **@options)
|
|
end
|
|
|
|
def to_s
|
|
if estimated_remaining_time = self.estimated_remaining_time
|
|
"#{@current}/#{@total} completed in #{Clock.formatted_duration(self.duration)}, #{Clock.formatted_duration(estimated_remaining_time)} remaining."
|
|
else
|
|
"#{@current}/#{@total} completed, waiting for estimate..."
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def duration_since_last_output
|
|
if @last_output_time
|
|
Progress.now - @last_output_time
|
|
end
|
|
end
|
|
|
|
def output?
|
|
if remaining.zero?
|
|
return true
|
|
elsif duration = duration_since_last_output
|
|
return duration > @minimum_output_duration
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|