Source

screentime-monitor / client_common.ml

Full commit
open Core.Std
open Async.Std

module Config = struct
  type t = { server: string
           ; user: Username.t sexp_option
           } 
  with sexp
end

let load_config () =
  match Unix.getenv "HOME" with
  | None -> failwith "no $HOME defined.  Can't open config"
  | Some home ->
    Reader.load_sexp (home ^/ ".screentime-monitor") Config.t_of_sexp
    >>| function
    | Error err ->
      failwiths "Failed to load config file" err <:sexp_of<Error.t>>
    | Ok config ->
      config
;;

let get_username () =
  let uid = Unix.getuid () in
  Unix.Passwd.getbyuid uid
  >>| function
  | None -> failwith "Could not compute username"
  | Some pwd -> Username.of_string pwd.Unix.Passwd.name
;;

let setup_conn username k =
  load_config ()
  >>= fun config ->
  (match username with
   | Some x -> return x
   | None ->
     match config.Config.user with
     | Some x -> return x
     | None -> get_username ())
  >>= fun username ->
  Common.with_rpc_conn
    ~host:config.Config.server
    ~port:Common.port
    (fun conn -> k username conn)

let username_spec = Command.Spec.Arg_type.create Username.of_string
let category_spec = Command.Spec.Arg_type.create Category.of_string

let on_term_signal =
  lazy (
    let stop_ivar = Ivar.create () in
    Signal.handle [Signal.term] ~f:(fun (_:Signal.t) ->
        Ivar.fill stop_ivar ());
    Ivar.read stop_ivar
  )

let shared_flags () =
  Command.Spec.(
    empty
    +> flag "-username" (optional username_spec)
      ~doc:"Username to act as"
  )

let days_flag () =
  Command.Spec.(
    flag "-days" (optional_with_default 4 int)
      ~doc:"number of days the report should go back"
  )

let edit_file sexpable ~editor ~tempfile =
  let rec loop () =
    Unix.system_exn (String.concat [editor;" ";tempfile])
    >>= fun () ->
    With_format.load tempfile sexpable
    >>= function
    | Ok resp -> return (Some resp)
    | Error e ->
      printf "Unable to read data:\n%s\n" (Error.to_string_hum e);
      printf "Try again? (Y/n): ";
      Reader.read_line (Lazy.force Reader.stdin)
      >>= fun response ->
      let reread =
        match response with
        | `Eof -> true
        | `Ok s ->
          match s |> String.lowercase |> String.strip with
          | "n" | "no" -> false
          | _ -> true
      in
      if not reread then (printf "Abandoning edit\n"; return None)
      else loop ()
  in
  loop ()
;;

let shell_run cmd args =
  In_thread.run(fun () ->
      Core_extended.Shell.run_full cmd args)

let clear_string =
  lazy (shell_run "clear" [])

let clear_screen () =
  force clear_string
  >>| fun clear_string ->
  print_endline clear_string