package ecaml

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file progress_reporter.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
open! Core
open! Async_kernel
open! Import

(* ['a] is a phantom type tracking whether the progress reporter was created with min/max
   values or not. *)
type 'a t =
  | Spinner : Value.t -> unit t
  | Range : Value.t -> int t
[@@deriving sexp_of]

let create_spinner_and_display =
  let make_progress_reporter =
    Funcall.Wrap.("make-progress-reporter" <: string @-> return value)
  in
  fun ~message -> Spinner (make_progress_reporter message)
;;

let create_with_range_and_display =
  let make_progress_reporter =
    Funcall.Wrap.("make-progress-reporter" <: string @-> int @-> int @-> return value)
  in
  fun ~min_incl ~max_incl ~message ->
    Range (make_progress_reporter message min_incl max_incl)
;;

let update_internal =
  let progress_reporter_force_update =
    Funcall.Wrap.(
      "progress-reporter-force-update"
      <: value @-> nil_or int @-> nil @-> nil_or string @-> return ignored)
  in
  let progress_reporter_update =
    Funcall.Wrap.(
      "progress-reporter-update"
      <: value @-> nil_or int @-> nil_or string @-> return ignored)
  in
  fun reporter progress suffix ->
    (* In tests, use [progress-reporter-force-update], which is deterministic because it
       does not check elapsed time. *)
    let f =
      match am_running_test with
      | false -> progress_reporter_update
      | true ->
        (* [progress-reporter-force-update] has an additional optional argument,
           [new-message], which allows one to change the message originally set at creation
           time.  Rewrap this function to make the types match. *)
        fun reporter value suffix ->
        progress_reporter_force_update reporter value () suffix
    in
    f reporter progress suffix
;;

let update (type a) ?suffix (t : a t) (progress : a) =
  match t with
  | Spinner reporter ->
    let () = progress in
    update_internal reporter None suffix
  | Range reporter -> update_internal reporter (Some progress) suffix
;;

let progress_reporter_done =
  Funcall.Wrap.("progress-reporter-done" <: value @-> return ignored)
;;

let finish (type a) (t : a t) =
  match t with
  | Spinner value | Range value -> progress_reporter_done value
;;

module Deferred = struct
  module List = struct
    let iter ?suffix ~message list ~how ~f =
      let total = List.length list in
      let t = create_with_range_and_display ~min_incl:0 ~max_incl:total ~message in
      (* We have to count instead of just using [iteri], since with concurrent loops they
         might be completed out of order. *)
      let done_so_far = ref 0 in
      let%bind () =
        Deferred.List.iter list ~how ~f:(fun elt ->
          let%bind () = f elt in
          let suffix = Option.map suffix ~f:(fun f -> f elt) in
          incr done_so_far;
          update ?suffix t !done_so_far;
          return ())
      in
      finish t;
      return ()
    ;;
  end
end
OCaml

Innovation. Community. Security.