From b927b63d128a1cdbb1517f7101484cefa91c4c71 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Sun, 29 Dec 2024 19:13:43 +0100 Subject: [PATCH] use prelude --- .ocamlformat | 5 ++- src/dune | 13 ++++++- src/sun.ml | 102 ++++++++++++++++++++++++++++++++------------------- 3 files changed, 79 insertions(+), 41 deletions(-) diff --git a/.ocamlformat b/.ocamlformat index c54116a..c365faf 100644 --- a/.ocamlformat +++ b/.ocamlformat @@ -1,4 +1,4 @@ -version=0.24.1 +version=0.27.0 assignment-operator=end-line break-cases=fit break-fun-decl=wrap @@ -24,8 +24,9 @@ let-and=sparse let-binding-spacing=compact let-module=compact margin=80 -max-indent=68 +max-indent=2 module-item-spacing=sparse +ocaml-version=4.14.0 ocp-indent-compat=false parens-ite=false parens-tuple=always diff --git a/src/dune b/src/dune index ad5ec95..7ee71f2 100644 --- a/src/dune +++ b/src/dune @@ -1,4 +1,13 @@ (executable (public_name sun) - (modules sun) - (libraries cmdliner directories scfg unix)) + (libraries + astring + cmdliner + directories + scfg + prelude + ptime + ptime.clock + rresult) + (flags + (:standard -open Prelude))) diff --git a/src/sun.ml b/src/sun.ml index 29f9493..fbf5854 100644 --- a/src/sun.ml +++ b/src/sun.ml @@ -6,10 +6,6 @@ module App_id = struct let application = "sun" end -let error msg = - Format.eprintf "error: %s@." msg; - exit 1 - (** Choose the correct filename output. *) let choose_output = function | None -> @@ -18,50 +14,84 @@ let choose_output = function let config_dir = let module Project_dirs = Directories.Project_dirs (App_id) in match Project_dirs.config_dir with - | None -> error "can't compute configuration directory path" - | Some config_dir -> config_dir + | None -> Fmt.failwith "can't compute configuration directory path" + | Some config_dir -> Fpath.v config_dir in - let file_name = Filename.concat config_dir "config.scfg" in + let file_name = Fpath.(config_dir / "config.scfg") in (* if the config file doesn't exist, then we use the empty configuration *) - if not @@ Sys.file_exists file_name then [] - else Result.fold ~error ~ok:Fun.id (Scfg.Parse.from_file file_name) + match Bos.OS.File.exists file_name with + | Error (`Msg e) -> Fmt.failwith "%s" e + | Ok false -> [] + | Ok true -> ( + match Scfg.Parse.from_file (Fpath.to_string file_name) with + | Error e -> Fmt.failwith "%s" e + | Ok file -> file ) in let output_dir = match Scfg.Query.get_dir "output_dir" config with | None -> - (* `output_dir` isn't specified in the user's configuration, we'll try the user's `picture_dir`. *) - let module User_dirs = Directories.User_dirs () in + let module + (* `output_dir` isn't specified in the user's configuration, we'll try the user's `picture_dir`. *) + User_dirs = + Directories.User_dirs () + in (* If we can't compute `picture_dir`, we use the current working directory. *) - Option.value User_dirs.picture_dir ~default:(Unix.getcwd ()) + begin + match User_dirs.picture_dir with + | Some dir -> begin + match Fpath.of_string dir with + | Error (`Msg e) -> Fmt.failwith "%s" e + | Ok dir -> dir + end + | None -> ( + match Bos.OS.Dir.current () with + | Error (`Msg e) -> Fmt.failwith "%s" e + | Ok dir -> dir ) + end | Some output_dir -> (* `output_dir` is specified in the user's config, we juste get its value. *) - Result.fold ~error ~ok:Fun.id (Scfg.Query.get_param 0 output_dir) + begin + match Scfg.Query.get_param 0 output_dir with + | Error e -> Fmt.failwith "%s" e + | Ok dir -> ( + match Bos.Pat.of_string dir with + | Error (`Msg e) -> Fmt.failwith "%s" e + | Ok pat -> ( + let s = + Bos.Pat.format + ~undef:(fun s -> + match Bos.OS.Env.var s with + | None -> Fmt.failwith "unbound environment variable %s" s + | Some v -> v ) + Astring.String.Map.empty pat + in + match Fpath.of_string s with + | Error (`Msg e) -> Fmt.failwith "%s" e + | Ok v -> v ) ) + end in (* Now that we have `output_dir`, we need to choose a file name. We'll use the current time. *) - let t = Unix.localtime @@ Unix.gettimeofday () in - let file_name = - Format.sprintf "%d%02d%02d_%02dh%02dm%02ds_sun.png" (1900 + t.tm_year) - (1 + t.tm_mon) t.tm_mday t.tm_hour t.tm_min t.tm_sec - in - Filename.concat output_dir file_name + let now = Ptime_clock.now () in + let (y, mo, d), ((h, m, s), _tz) = Ptime.to_date_time now in + let file_name = Fmt.str "%d%02d%02d_%02dh%02dm%02ds_sun.png" y mo d h m s in + Fpath.(output_dir / file_name) | Some output_file -> output_file let screenshot output_file = let output_file = choose_output output_file in let region = - let chan = Unix.open_process_in "slurp" in - try - let output = input_line chan in - match Unix.close_process_in chan with - | WEXITED 0 -> output - | _err -> error "slurp failed" - with End_of_file -> error "slurp failed" + (* TODO: better error handling to avoid ignoring the error when slup fails... *) + match Bos.OS.Cmd.run_out Bos.Cmd.(v "slurp") |> Bos.OS.Cmd.to_string with + | Error (`Msg e) -> Fmt.failwith "%s" e + | Ok region -> region in - let cmd = Format.sprintf {|grim -g "%s" "%s"|} region output_file in - let chan = Unix.open_process_in cmd in - match Unix.close_process_in chan with - | WEXITED 0 -> Format.printf "screenshot saved at %s@\n" output_file - | _err -> error "grim failed" + match + Bos.OS.Cmd.run_status Bos.Cmd.(v "grim" % "-g" % region % p output_file) + with + | Error (`Msg e) -> Fmt.failwith "%s" e + | Ok (`Exited 0) -> Fmt.pr "screenshot saved at %a@\n" Fpath.pp output_file + | Ok (`Exited n) -> Fmt.epr "grim exited with code %d@\n" n + | Ok (`Signaled n) -> Fmt.epr "grim got (OCaml) signal %d@\n" n let cli = let open Cmdliner in @@ -74,12 +104,10 @@ let cli = in let output_file = let doc = "output file" in - let parse s = Ok (Some s) in - Arg.( - value - & pos 0 - (conv (parse, Format.pp_print_option Format.pp_print_string)) - None (info [] ~doc) ) + let parse s = + match Fpath.of_string s with Error _ as e -> e | Ok p -> Ok (Some p) + in + Arg.(value & pos 0 (conv (parse, Fmt.option Fpath.pp)) None (info [] ~doc)) in Cmd.v info Term.(const screenshot $ output_file)