gix_credentials/program/
mod.rs1use std::process::{Command, Stdio};
2
3use bstr::{BString, ByteSlice, ByteVec};
4
5use crate::{helper, Program};
6
7#[derive(Debug, Clone, Eq, PartialEq)]
9pub enum Kind {
10 Builtin,
12 ExternalName {
14 name_and_args: BString,
18 },
19 ExternalPath {
21 path_and_args: BString,
24 },
25 ExternalShellScript(BString),
27}
28
29impl Program {
31 pub fn from_kind(kind: Kind) -> Self {
33 Program {
34 kind,
35 child: None,
36 stderr: true,
37 }
38 }
39
40 pub fn from_custom_definition(input: impl Into<BString>) -> Self {
43 fn from_custom_definition_inner(mut input: BString) -> Program {
44 let kind = if input.starts_with(b"!") {
45 input.remove(0);
46 Kind::ExternalShellScript(input)
47 } else {
48 let path = gix_path::from_bstr(
49 input
50 .find_byte(b' ')
51 .map_or(input.as_slice(), |pos| &input[..pos])
52 .as_bstr(),
53 );
54 if gix_path::is_absolute(path) {
55 Kind::ExternalPath { path_and_args: input }
56 } else {
57 Kind::ExternalName { name_and_args: input }
58 }
59 };
60 Program {
61 kind,
62 child: None,
63 stderr: true,
64 }
65 }
66 from_custom_definition_inner(input.into())
67 }
68
69 pub fn to_command(&self, action: &helper::Action) -> std::process::Command {
71 let git_program = gix_path::env::exe_invocation();
72 let mut cmd = match &self.kind {
73 Kind::Builtin => {
74 let mut cmd = Command::from(gix_command::prepare(git_program));
75 cmd.arg("credential").arg(action.as_arg(false));
76 cmd
77 }
78 Kind::ExternalName { name_and_args } => {
79 let mut args = name_and_args.clone();
80 args.insert_str(0, "credential-");
81 args.insert_str(0, " ");
82 args.insert_str(0, git_program.to_string_lossy().as_ref());
83 gix_command::prepare(gix_path::from_bstr(args.as_bstr()).into_owned())
84 .arg(action.as_arg(true))
85 .command_may_be_shell_script_allow_manual_argument_splitting()
86 .into()
87 }
88 Kind::ExternalShellScript(for_shell)
89 | Kind::ExternalPath {
90 path_and_args: for_shell,
91 } => gix_command::prepare(gix_path::from_bstr(for_shell.as_bstr()).as_ref())
92 .command_may_be_shell_script()
93 .arg(action.as_arg(true))
94 .into(),
95 };
96 cmd.stdin(Stdio::piped())
97 .stdout(if action.expects_output() {
98 Stdio::piped()
99 } else {
100 Stdio::null()
101 })
102 .stderr(if self.stderr { Stdio::inherit() } else { Stdio::null() });
103 cmd
104 }
105}
106
107impl Program {
109 pub fn suppress_stderr(mut self) -> Self {
111 self.stderr = false;
112 self
113 }
114}
115
116impl Program {
117 pub(crate) fn start(
118 &mut self,
119 action: &helper::Action,
120 ) -> std::io::Result<(std::process::ChildStdin, Option<std::process::ChildStdout>)> {
121 assert!(self.child.is_none(), "BUG: must not call `start()` twice");
122 let mut cmd = self.to_command(action);
123 gix_trace::debug!(cmd = ?cmd, "launching credential helper");
124 let mut child = cmd.spawn()?;
125 let stdin = child.stdin.take().expect("stdin to be configured");
126 let stdout = child.stdout.take();
127
128 self.child = child.into();
129 Ok((stdin, stdout))
130 }
131
132 pub(crate) fn finish(&mut self) -> std::io::Result<()> {
133 let mut child = self.child.take().expect("Call `start()` before calling finish()");
134 let status = child.wait()?;
135 if status.success() {
136 Ok(())
137 } else {
138 Err(std::io::Error::new(
139 std::io::ErrorKind::Other,
140 format!("Credentials helper program failed with status code {:?}", status.code()),
141 ))
142 }
143 }
144}
145
146pub mod main;
148pub use main::function::main;