···11+(** Progress reporting for sync operations.
22+33+ Provides terminal progress indicators for long-running sync phases.
44+55+ Note: For thread-safety during parallel operations, uses an atomic counter
66+ that's rendered on each tick. *)
77+88+type t = {
99+ progress : Tty.Progress.t;
1010+ completed : int Atomic.t;
1111+ total : int;
1212+ phase_name : string;
1313+}
1414+1515+let v ~total phase_name =
1616+ let msg = Fmt.str "%s (0/%d)" phase_name total in
1717+ let progress = Tty.Progress.create ~total msg in
1818+ { progress; completed = Atomic.make 0; total; phase_name }
1919+2020+let tick t name =
2121+ let n = Atomic.fetch_and_add t.completed 1 + 1 in
2222+ let msg = Fmt.str "%s: %s (%d/%d)" t.phase_name name n t.total in
2323+ Tty.Progress.message t.progress msg;
2424+ Tty.Progress.set t.progress n
2525+2626+let clear t = Tty.Progress.clear t.progress
2727+let finish t = Tty.Progress.finish t.progress
2828+2929+(** Module signature for progress implementations *)
3030+module type S = sig
3131+ type t
3232+3333+ val v : total:int -> string -> t
3434+ val tick : t -> string -> unit
3535+ val clear : t -> unit
3636+ val finish : t -> unit
3737+end
3838+3939+(** Active progress with terminal output *)
4040+module Active : S with type t = t = struct
4141+ type nonrec t = t
4242+4343+ let v = v
4444+ let tick = tick
4545+ let clear = clear
4646+ let finish = finish
4747+end
4848+4949+(** Disabled progress (no terminal output) *)
5050+module Disabled : S with type t = unit = struct
5151+ type t = unit
5252+5353+ let v ~total:_ _ = ()
5454+ let tick () _ = ()
5555+ let clear () = ()
5656+ let finish () = ()
5757+end
+71
lib/progress.mli
···11+(** Progress reporting for sync operations.
22+33+ Provides terminal progress indicators for long-running sync phases. Uses
44+ {!Tty.Progress} for display when enabled, or no-op when disabled (quiet
55+ mode, non-TTY output).
66+77+ {2 Usage}
88+99+ {[
1010+ let progress = Progress.v ~total:10 "Fetching" in
1111+ List.iter
1212+ (fun name ->
1313+ do_work name;
1414+ Progress.tick progress name)
1515+ items;
1616+ Progress.finish progress
1717+ ]}
1818+1919+ Or with the functor interface for conditional progress:
2020+2121+ {[
2222+ let module P =
2323+ (val if show_progress then (module Progress.Active)
2424+ else (module Progress.Disabled) : Progress.S)
2525+ in
2626+ let p = P.v ~total "Fetching" in
2727+ ...;
2828+ P.finish p
2929+ ]} *)
3030+3131+type t
3232+(** Progress state for a sync phase. *)
3333+3434+val v : total:int -> string -> t
3535+(** [v ~total phase_name] creates progress for a phase with [total] items. Shows
3636+ as "[phase_name] (0/[total])" initially. *)
3737+3838+val tick : t -> string -> unit
3939+(** [tick t name] increments progress and updates message to show [name]. *)
4040+4141+val clear : t -> unit
4242+(** [clear t] clears the progress line without printing newline. *)
4343+4444+val finish : t -> unit
4545+(** [finish t] completes the progress bar and prints newline. *)
4646+4747+(** {1 Abstract Interface}
4848+4949+ For code that needs to conditionally enable progress. *)
5050+5151+module type S = sig
5252+ type t
5353+5454+ val v : total:int -> string -> t
5555+ (** Create progress for a phase with the given total item count. *)
5656+5757+ val tick : t -> string -> unit
5858+ (** Increment progress and update the displayed message. *)
5959+6060+ val clear : t -> unit
6161+ (** Clear the progress line without printing a newline. *)
6262+6363+ val finish : t -> unit
6464+ (** Complete the progress bar and print a newline. *)
6565+end
6666+6767+module Active : S with type t = t
6868+(** Active progress with terminal output. *)
6969+7070+module Disabled : S with type t = unit
7171+(** Disabled progress (no terminal output). *)