craigfe / progress

Progress bar library for OCaml
MIT License
137 stars 15 forks source link

Lwt support? #25

Open Lupus opened 2 years ago

Lupus commented 2 years ago

Is it possible to use this fantastic progress bar libary with Lwt application? I suspect that "Manual lifecycle management" API is something that should help, but probably some easy to consume example in documentation will greatly help!

yashrk commented 2 years ago

@Lupus looks like it's possible, but tricky a bit because to update a progress bar outside of the Progress.with_reporter function we need to extract a reporter from a Progress.Display instance. And the Progress.Display.reporters function returns a Lisp-like heterogeneous list Progress.Reporter.list, not the OCaml one.

I tried to reproduce the example from https://craigfe.github.io/progress/progress/Progress/index.html without the Progress.with_reporter call and with Lwt I/O, so that's what I get:

let str_config =
  Progress.Config.v
    ~ppf: Format.str_formatter
    ~hide_cursor: false
    ~persistent: true
    ~max_width: None
    ~min_interval: None
    ()

let main () =
  let bar ~total =
    let open Progress.Line in
    list [ const "Progress bar"; spinner (); bar ~style: `UTF8 total; percentage_of total ]
  in
  let sequence = Progress.Multi.line (bar ~total: 100) in
  let display = Progress.Display.start ~config: str_config sequence in
  let (reporter :: []) = Progress.Display.reporters display in

  let%lwt () = Lwt_io.printf "%s\n%!" @@ Format.flush_str_formatter () in
  Terminal.Ansi.move_up Format.str_formatter 1;

  let rec update_loop counter =
    if counter < 100
    then begin
        let%lwt _t = Lwt_unix.sleep 0.1 in
        reporter 1;
        let%lwt () = Lwt_io.printf "%s\n%!" @@ Format.flush_str_formatter () in
        Terminal.Ansi.move_up Format.str_formatter 1;
        update_loop (counter + 1)
      end
    else Lwt.return_unit in
  let%lwt () = update_loop 0
  in
  Lwt.return_unit

let () =
  Lwt_main.run @@ main ()

It works, but maybe @CraigFe can advise a better solution.

dsincl12 commented 1 year ago

@Lupus looks like it's possible, but tricky a bit because to update a progress bar outside of the Progress.with_reporter function we need to extract a reporter from a Progress.Display instance. And the Progress.Display.reporters function returns a Lisp-like heterogeneous list Progress.Reporter.list, not the OCaml one.

I tried to reproduce the example from https://craigfe.github.io/progress/progress/Progress/index.html without the Progress.with_reporter call and with Lwt I/O, so that's what I get:

let str_config =
  Progress.Config.v
    ~ppf: Format.str_formatter
    ~hide_cursor: false
    ~persistent: true
    ~max_width: None
    ~min_interval: None
    ()

let main () =
  let bar ~total =
    let open Progress.Line in
    list [ const "Progress bar"; spinner (); bar ~style: `UTF8 total; percentage_of total ]
  in
  let sequence = Progress.Multi.line (bar ~total: 100) in
  let display = Progress.Display.start ~config: str_config sequence in
  let (reporter :: []) = Progress.Display.reporters display in

  let%lwt () = Lwt_io.printf "%s\n%!" @@ Format.flush_str_formatter () in
  Terminal.Ansi.move_up Format.str_formatter 1;

  let rec update_loop counter =
    if counter < 100
    then begin
        let%lwt _t = Lwt_unix.sleep 0.1 in
        reporter 1;
        let%lwt () = Lwt_io.printf "%s\n%!" @@ Format.flush_str_formatter () in
        Terminal.Ansi.move_up Format.str_formatter 1;
        update_loop (counter + 1)
      end
    else Lwt.return_unit in
  let%lwt () = update_loop 0
  in
  Lwt.return_unit

let () =
  Lwt_main.run @@ main ()

It works, but maybe @craigfe can advise a better solution.

This works, but timings are off for bytes_per_sec :)