Coverage report: /home/ellis/comp/core/lib/cli/progress.lisp
Kind | Covered | All | % |
expression | 18 | 218 | 8.3 |
branch | 1 | 22 | 4.5 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; lib/cli/progress.lisp --- Progress Bars
3
;; from https://github.com/sirherrbatka/cl-progress-bar
6
(in-package :cli/progress)
8
(defclass progress-bar ()
16
:initarg :progress-character
17
:accessor progress-character)
19
:initarg :character-count
20
:accessor character-count
21
:documentation "How many characters wide is the progress bar?")
23
:initarg :characters-so-far
24
:accessor characters-so-far)
26
:initarg :update-interval
27
:accessor update-interval
28
:documentation "Update the progress bar display after this many
29
internal-time units.")
31
:initarg :last-update-time
32
:accessor last-update-time
33
:documentation "The display was last updated at this time.")
37
:documentation "The total number of units tracked by this progress bar.")
41
:documentation "How far in the progress are we?")
44
:reader progress-mutex
45
:documentation "Just a mutex, allows progress bar to be thread safe.")
49
:documentation "How many raw units should be tracked in the next
52
:mutex (sb-thread:make-mutex)
53
:progress-character #\=
56
:update-interval (floor internal-time-units-per-second 4)
62
(defgeneric start-progress-display (progress-bar))
63
(defgeneric update-progress (progress-bar unit-count))
64
(defgeneric update-progress-display (progress-bar))
65
(defgeneric finish-progress-display (progress-bar))
66
(defgeneric elapsed-time (progress-bar))
67
(defgeneric units-per-second (progress-bar))
69
(defmethod start-progress-display (progress-bar)
70
(setf (last-update-time progress-bar) (get-internal-real-time))
71
(setf (start-time progress-bar) (get-internal-real-time))
75
(defmethod update-progress-display (progress-bar)
76
(incf (progress progress-bar) (pending progress-bar))
77
(setf (pending progress-bar) 0)
78
(setf (last-update-time progress-bar) (get-internal-real-time))
79
(unless (zerop (progress progress-bar))
80
(let* ((showable (floor (character-count progress-bar)
81
(/ (total progress-bar) (progress progress-bar))))
82
(needed (- showable (characters-so-far progress-bar))))
83
(setf (characters-so-far progress-bar) showable)
85
(write-char (progress-character progress-bar)))
88
(defmethod update-progress (progress-bar unit-count)
89
(incf (pending progress-bar) unit-count)
90
(let ((now (get-internal-real-time)))
91
(when (< (update-interval progress-bar)
92
(- now (last-update-time progress-bar)))
93
(update-progress-display progress-bar))))
95
(defconstant +seconds-in-one-hour+ 3600)
96
(defconstant +seconds-in-one-minute+ 60)
98
(defun time-in-seconds-minutes-hours (in-seconds)
99
(unless (zerop in-seconds)
100
(format t "Finished in")
101
(when (>= in-seconds +seconds-in-one-hour+)
102
(let* ((hours (floor (/ in-seconds +seconds-in-one-hour+))))
103
(decf in-seconds (* hours +seconds-in-one-hour+))
104
(format t " ~a hour~p" hours hours)))
105
(when (>= in-seconds +seconds-in-one-minute+)
106
(let* ((minutes (floor (/ in-seconds +seconds-in-one-minute+))))
107
(decf in-seconds (* minutes +seconds-in-one-minute+))
108
(format t " ~a minute~p" minutes minutes)))
109
(unless (zerop in-seconds) (format t " ~$ seconds" in-seconds)))
112
(defmethod finish-progress-display (progress-bar)
113
(update-progress-display progress-bar)
114
(setf (end-time progress-bar) (get-internal-real-time))
116
(time-in-seconds-minutes-hours (elapsed-time progress-bar))
118
(unless (= (progress progress-bar) (total progress-bar))
119
(warn "Expected TOTAL is ~a but progress at the moment of finishing is ~a"
121
(progress progress-bar))))
123
(defmethod elapsed-time (progress-bar)
124
(/ (- (end-time progress-bar) (start-time progress-bar))
125
internal-time-units-per-second))
127
(defmethod units-per-second (progress-bar)
128
(if (plusp (elapsed-time progress-bar))
129
(/ (total progress-bar) (elapsed-time progress-bar))
132
(defparameter *uncertain-progress-chars* "?")
134
(defclass uncertain-size-progress-bar (progress-bar)
135
((progress-char-index
136
:initarg :progress-char-index
137
:accessor progress-char-index)
139
:initarg :units-per-char
140
:accessor units-per-char))
143
:progress-char-index 1
144
:units-per-char (floor (expt 1024 2) 50)))
146
(defmethod update-progress :after ((progress-bar uncertain-size-progress-bar)
148
(incf (total progress-bar) unit-count))
150
(defmethod progress-character ((progress-bar uncertain-size-progress-bar))
151
(let ((index (progress-char-index progress-bar)))
153
(char *uncertain-progress-chars* index)
154
(setf (progress-char-index progress-bar)
155
(mod (1+ index) (length *uncertain-progress-chars*))))))
157
(defmethod update-progress-display ((progress-bar uncertain-size-progress-bar))
158
(setf (last-update-time progress-bar) (get-internal-real-time))
159
(multiple-value-bind (chars pend)
160
(floor (pending progress-bar) (units-per-char progress-bar))
161
(setf (pending progress-bar) pend)
163
(write-char (progress-character progress-bar))
164
(incf (characters-so-far progress-bar))
165
(when (<= (character-count progress-bar)
166
(characters-so-far progress-bar))
168
(setf (characters-so-far progress-bar) 0)
172
(defvar *progress-bar* nil)
173
(defparameter *progress-bar-enabled* nil)
175
(declaim (inline update))
176
(defun update! (unit-count &optional (progress-bar *progress-bar*))
177
(check-type unit-count (integer 1 *))
178
(check-type progress-bar (or null progress-bar))
179
(unless (null progress-bar)
180
(sb-thread:with-mutex ((progress-mutex progress-bar))
181
(update-progress progress-bar unit-count))))
183
(defun make-progress-bar (total)
184
(check-type total (or null (integer 0 *)))
185
(if (or (not total) (zerop total))
186
(make-instance 'uncertain-size-progress-bar)
187
(make-instance 'progress-bar
190
(defmacro with-progress-bar ((steps-count description &rest desc-args) &body body)
191
(let ((!old-bar (gensym)))
192
`(let* ((,!old-bar *progress-bar*)
193
(*progress-bar* (or ,!old-bar
194
(when *progress-bar-enabled*
195
(make-progress-bar ,steps-count)))))
196
(unless (eq ,!old-bar *progress-bar*)
198
(format t ,description ,@desc-args)
199
(start-progress-display *progress-bar*))
200
(prog1 (progn ,@body)
201
(unless (eq ,!old-bar *progress-bar*)
202
(finish-progress-display *progress-bar*))))))
204
(defmacro with-progress-maybe (enabled (steps-count description &rest desc-args) &body body)
205
(declare (ignorable steps-count description desc-args))
207
(with-progress-bar (,steps-count ,description ,@desc-args) ,@body)