struct

    let discriminate_process_status s ret =
      begin match ret with
      | Lwt_unix.WEXITED 0 -> return ()
      | Lwt_unix.WEXITED n -> fail (`Shell (s, `Exited n))
      | Lwt_unix.WSIGNALED n -> fail (`Shell (s, `Signaled n))
      | Lwt_unix.WSTOPPED n -> fail (`Shell (s, `Stopped n))
    end

    let status_to_string = function
    | `Exited i -> sprintf "Exited with %d" i
    | `Exn e -> sprintf "Exception %s" (exn e)
    | `Signaled i -> sprintf "Signaled (%d)" i
    | `Stopped i -> sprintf "Stopped (%d)" i

    let do_or_fail s =
      wrap_deferred  Lwt_io.(fun () -> Lwt_unix.system s)
        ~on_exn:(fun e -> `Shell (s, `Exn e))
      >>= fun ret ->
      discriminate_process_status s ret


    let execute s =
      wrap_deferred ~on_exn:(fun e -> `Shell (s, `Exn e))
        Lwt.(fun () ->
          let inprocess = Lwt_process.(open_process_full (shell s)) in
          Lwt_list.map_p Lwt_io.read
            [inprocess#stdout; inprocess#stderr; ]
          >>= fun output ->
          inprocess#status >>= fun status ->
          return (status, output))
      >>= fun (ret, output) ->
      let code =
        match ret with
        | Lwt_unix.WEXITED n ->   (`Exited n)
        | Lwt_unix.WSIGNALED n -> (`Signaled n)
        | Lwt_unix.WSTOPPED n ->  (`Stopped n)
      in
      begin match output with
      | [out; err] -> return (out, err, code)
      | _ -> assert false
      end

  end