This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user