package progress

  1. Overview
  2. Docs
type 'a t

The type of progress lines for reported values of type 'a. This module provides a selection of individual line segments that can be combined to produce more interesting layouts. You may wish to look over the examples for inspiration.

Basic line segments

val const : string -> _ t

const s is the segment that always displays s.

val constf : ('a, Format.formatter, unit, _ t) format4 -> 'a

Like const, but takes a format string and corresponding arguments. constf "..." a b c ... is equivalent to const (Format.asprintf "..." a b c ...), except that colours added with Fmt.styled are not discarded.

val string : string t

A line segment that displays a dynamically-sized string message. Use lpad and rpad to pad the message up to a given length.

val lpad : int -> 'a t -> 'a t

lpad n t left-pads the segment t to size n by adding blank space at the start.

val rpad : int -> 'a t -> 'a t

rpad n t right-pads the segment t to size n by adding blank space at the end.

val of_printer : ?init:'a -> 'a Printer.t -> 'a t

of_printer p is a segment that renders the latest reported value using printer p. See sum for a variant that reports accumulated values instead.

Counting segments

These segments all consume integer values and display the accumulated total of reported values in some way. The top-level Line segments are specialised to int values; see "Alternative integer types" for variants supporting int32, int64 etc.

val count_to : ?pp:int Printer.t -> ?sep:unit t -> int -> int t

count_to target renders both the current running total of reported values and the fixed value target, separated by the the given separator, i.e. 42/100. sep defaults to const "/".

val bytes : int t

Prints the running total as a number of bytes, using ISO/IEC binary prefixes (e.g. 10.4 MiB). See also bytes_per_sec.

val percentage_of : int -> int t

percentage_of target renders the running total as a percentage of target, i.e. 42%. Values outside the range [0, 100] will be clamped to either 0 or 100.

val sum : ?pp:int Printer.t -> width:int -> unit -> int t

sum ~width () displays a running total of reported values using width-many terminal columns. If passed, pp overrides the printer used for rendering the count.

Graphical segments

module Bar_style : sig ... end
val bar : ?style:[ `ASCII | `UTF8 | `Custom of Bar_style.t ] -> ?color:Color.t -> ?width:[ `Fixed of int | `Expand ] -> ?data:[ `Sum | `Latest ] -> int -> int t

bar total is a progress bar of the form: [#################...............]

The proportion of the bar that is filled is given by <reported_so_far> / total. Optional parameters are as follows:

  • ?style specifies whether to use a UTF-8 or an ASCII encoding for the progress bar. The UTF-8 encoding shows a higher resolution of progress, but may not be supported in all terminals. The default is `ASCII.
  • ?color causes the filled portion of the bar to be rendered with the given colour. (Equivalent to setting the colour with Bar_style.with_color.)
  • ?width is the width of the bar in columns. Defaults to `Expand, which causes the bar to occupy the remaining rendering space after accounting for other line segments on the same line.
  • ?data changes the metric that is indicated by the progress bar. `Sum (the default) causes the progress bar to correspond to the running total of values reported so far. `Latest causes each reported value to overwrite the previous one instead.
val spinner : ?frames:string list -> ?color:Color.t -> ?min_interval:Duration.t option -> unit -> _ t

spinner () is a small segment that cycles over a fixed number of frames each time a value is reported. e.g.

⠋ → ⠙ → ⠹ → ⠸ → ⠼ → ⠴ → ⠦ → ⠧ → ⠇ → ⠏ → ...

Optional prameters are as follows:

  • ?frames alters the sequence of frames rendered by the spinner;
  • ?color causes each frame to be rendered with the given colour;
  • ?min_interval is the minimum time interval between frame transitions of the spinner (i.e. a debounce threshold). The default is Some 80ms.

Time-sensitive segments

val bytes_per_sec : int t

bytes_per_sec renders the rate of change of the running total as a number of bytes per second, using ISO/IEC binary prefixes (e.g. 10.4 MiB/s).

val elapsed : ?pp:Duration.t Printer.t -> unit -> _ t

Displays the time for which the bar has been rendering in MM:SS form.

val eta : ?pp:Duration.t Printer.t -> int -> int t

Displays an estimate of the remaining time until total is accumulated by the reporters, in MM:SS form.

val rate : float Printer.t -> int t

rate pp is an integer segment that uses pp to print the rate of reported values per second. (For instance, bytes_per_sec is rate Units.Bytes.of_float.)

Combining segments

val (++) : 'a t -> 'a t -> 'a t

Horizontally join two segments of the same reported value type.

val list : ?sep:'a t -> 'a t list -> 'a t

Horizontally join a list of segments, with a given separator. sep defaults to const " ".

val pair : ?sep:unit t -> 'a t -> 'b t -> ('a * 'b) t

Horizontally join a pair of segments consuming different reported values into a single segment that consumes a pair.

val using : ('a -> 'b) -> 'b t -> 'a t

using f s is a segment that first applies f to the reported value and then behaves as segment s.


The following line segments are definable in terms of the others, but provided for convenience:

val parens : 'a t -> 'a t

parens t is const "(" ++ t ++ const ")".

val brackets : 'a t -> 'a t

brackets t is const "[" ++ t ++ const "]".

val braces : 'a t -> 'a t

braces t is const "{" ++ t ++ const "}".

val noop : unit -> _ t

A zero-width line segment that does nothing. This segment will not be surrounded with separators when used in a list, making it a useful "off" state for conditionally-enabled segments.

val spacer : int -> _ t

spacer n is const (String.make n ' ').

val ticker_to : ?sep:unit t -> int -> _ t

ticker_to total is using ~f:(fun _ -> 1) (counter_to total). i.e. it renders the total number of reported values of some arbitrary type.

Alternative integer types

Many of the line segments above are specialised to int values for simplicity (and performance), but certain use-cases may require different types (e.g. for file transfers greater than 2 GiB on 32-bit platforms). The following modules re-export the Line DSL with different integer specialisations, and are intended to be opened locally, e.g.

let my_line =
  let open Progress.Line.Using_int64 in
  list [ const "Downloading large file"; bar total; bytes ]
module Integer_dependent : sig ... end
module Using_int32 : Integer_dependent.Ext with type integer := int32
module Using_int64 : Integer_dependent.Ext with type integer := int64
module Using_float : Integer_dependent.Ext with type integer := float


(* Renders: "[######---------------------------------------]  14/100" *)
bar 100 ++ const " " ++ count_to 100
(* Renders: "⠏ [01:04] [####-----------------]  293.9 MiB (eta: 07:12)" *)
  [ spinner ()
  ; brackets (elapsed ())
  ; bar total
  ; bytes
  ; parens (const "eta: " ++ eta total)
(* Renders: "  a.txt │██████████████████████████▋   │   91.4 MiB/s  92%" *)
  [ lpad 7 (const file_name)
  ; bar ~style:`UTF8 total
  ; bytes_per_sec
  ; percentage_of total

See the examples/ directory of the source repository for more.

Library internals

module Internals : sig ... end

Exposes the underlying implementation of line segments for testing. This API is unstable, unsafe and mostly undocumented; here be dragons etc.