package hg_lib

  1. Overview
  2. Docs

Source file hg_lib_factory.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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
open Core
module Unix = Core_unix
include Hg_lib_factory_intf

module With_global_args = struct
  (* other global flags mainly change the output (e.g. --verbose) or don't matter for most
     commands in this module (e.g. --noninteractive) *)
  type 'a t =
    ?repository:string
    -> ?cwd:string
    -> ?config:(string * string) list
    -> ?env:Async.Process.env
    -> 'a

  let map t ~f ?repository ?cwd ?config ?env = f (t ?repository ?cwd ?config ?env)

  let prepend_to_args ~repository ~cwd ~config args =
    List.concat
      [ (match repository with
         | None -> []
         | Some repo -> [ "--repository"; repo ])
      ; (match cwd with
         | None -> []
         | Some cwd -> [ "--cwd"; cwd ])
      ; (match config with
         | None -> []
         | Some config ->
           List.concat_map config ~f:(fun (key, data) -> [ "--config"; key ^ "=" ^ data ]))
      ; args
      ]
  ;;
end

module With_global_args_remote = struct
  type 'a t =
    server:Command_server.t
    -> ?repository:string
    -> ?cwd:string
    -> ?config:(string * string) list
    -> 'a

  let map t ~f ~server ?repository ?cwd ?config = f (t ~server ?repository ?cwd ?config)

  (* This doesn't take [~cwd] because [Command_server.run] itself handles [~cwd]. *)
  let prepend_to_args ~repository ~config args =
    List.concat
      [ (match repository with
         | None -> []
         | Some repo -> [ "--repository"; repo ])
      ; (match config with
         | None -> []
         | Some config ->
           List.concat_map config ~f:(fun (key, data) -> [ "--config"; key ^ "=" ^ data ]))
      ; args
      ]
  ;;
end

let handle_output_with_args ~args handle_output (output : Async.Process.Output.t) =
  Hg_private.Or_simple_error.tag
    (handle_output output)
    "hg error"
    args
    [%sexp_of: string list]
;;

let handle_output_exn ~args handle_output output =
  Or_error.ok_exn (handle_output_with_args ~args handle_output output)
;;

module Simple = struct
  module With_args = With_global_args

  module Output = struct
    type 'a t = 'a

    let return = Fn.id
  end

  let run ?repository ?cwd ?config ?env ~args ~handle_output () =
    let args = With_global_args.prepend_to_args ~repository ~cwd ~config args in
    let { Unix.Process_info.stdin; stdout; stderr; pid } =
      match env with
      | None -> Unix.create_process ~prog:"hg" ~args
      | Some env -> Unix.create_process_env ~prog:"hg" ~args ~env ()
    in
    let stdout_s = In_channel.input_all (Unix.in_channel_of_descr stdout) in
    let stderr_s = In_channel.input_all (Unix.in_channel_of_descr stderr) in
    let exit_status = Unix.waitpid pid in
    Unix.close stdin;
    Unix.close stdout;
    Unix.close stderr;
    handle_output_exn
      ~args
      handle_output
      { exit_status; stdout = stdout_s; stderr = stderr_s }
  ;;
end

open Async (* do this before locally redefining Async *)

module Async = struct
  module With_args = With_global_args

  module Output = struct
    type 'a t = 'a Or_error.t Deferred.t

    let return x = return (Ok x)
  end

  let run ?repository ?cwd ?config ?env ~args ~handle_output () =
    let args = With_global_args.prepend_to_args ~repository ~cwd ~config args in
    Process.create ?env ~prog:"hg" ~args ()
    >>=? fun process ->
    Process.collect_output_and_wait process
    >>| fun output -> handle_output_with_args ~args handle_output output
  ;;
end

module Fixed_hg_environment (E : Hg_env) = struct
  module With_args = With_global_args

  module Output = struct
    type 'a t = 'a Or_error.t Deferred.t

    let return x = return (Ok x)
  end

  let run ?repository ?cwd ?config ?env ~args ~handle_output () =
    let config = Option.map config ~f:(fun config -> E.hg_config_options @ config) in
    let args = With_global_args.prepend_to_args ~repository ~cwd ~config args in
    let env =
      let tuples = [ "HGRCPATH", E.hgrc_path; "HGUSER", Lazy.force E.hg_user ] in
      match env with
      | None -> `Extend tuples
      | Some (`Extend envs) -> `Extend (tuples @ envs)
      | Some (`Override l) -> `Override (List.map tuples ~f:(fun (x, y) -> x, Some y) @ l)
      | Some (`Replace envs) -> `Replace (tuples @ envs)
      | Some (`Replace_raw envs) ->
        let env_strings = List.map tuples ~f:(fun (key, value) -> key ^ "=" ^ value) in
        `Replace_raw (env_strings @ envs)
    in
    if false
    then
      [%log.global.debug_format
        !"[%{sexp:Process.env}] %s %{sexp:string list}" env E.hg_binary args];
    Process.create ~env ~prog:E.hg_binary ~args ()
    >>=? fun process ->
    Process.collect_output_and_wait process
    >>| fun output -> handle_output_with_args ~args handle_output output
  ;;
end

module Remote = struct
  module With_args = With_global_args_remote
  module Output = Deferred.Or_error

  let run ~server ?repository ?(cwd = ".") ?config ~args ~handle_output () =
    let args = With_global_args_remote.prepend_to_args ~repository ~config args in
    Command_server.run_command server ~cwd args
    >>=? fun output -> return (handle_output_with_args ~args handle_output output)
  ;;
end

module Make_lib (M : Make_s) = struct
  module type S = sig
    module Make (A : Arg) : M(A).S
    module Simple : M(Simple).S
    module Async : M(Async).S
    module Fixed_hg_environment (E : Hg_env) : M(Fixed_hg_environment(E)).S
    module Remote : M(Remote).S
  end
end
OCaml

Innovation. Community. Security.