Unified · Split
| @@ -6,6 +6,10 @@ pub enum Kind { · 5 unmodified lines | |||
| 6 | GitCredential, | 6 | GitCredential, |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | /// Additional context to be passed to the credentials helper. | ||
| 10 | // TODO: fill in what's needed per configuration | ||
| 11 | pub struct Context; | ||
| 12 | |||
| 9 | mod error { | 13 | mod error { |
| 10 | /// The error used in the [credentials helper][crate::helper()]. | 14 | /// The error used in the [credentials helper][crate::helper()]. |
| 11 | #[derive(Debug, thiserror::Error)] | 15 | #[derive(Debug, thiserror::Error)] |
| @@ -15,8 +19,8 @@ mod error { · 3 unmodified lines | |||
| 15 | Io(#[from] std::io::Error), | 19 | Io(#[from] std::io::Error), |
| 16 | #[error("Could not find {name:?} in output of credentials helper")] | 20 | #[error("Could not find {name:?} in output of credentials helper")] |
| 17 | KeyNotFound { name: String }, | 21 | KeyNotFound { name: String }, |
| 18 | #[error("Credentials helper program failed with status code {code:?}")] | 22 | #[error(transparent)] |
| 19 | CredentialsHelperFailed { code: Option<i32> }, | 23 | CredentialsHelperFailed { source: std::io::Error }, |
| 20 | } | 24 | } |
| 21 | } | 25 | } |
| 22 | pub use error::Error; | 26 | pub use error::Error; |
| @@ -80,7 +84,7 @@ pub struct Outcome { · 57 unmodified lines | |||
| 80 | } | 84 | } |
| 81 | 85 | ||
| 82 | pub(crate) mod function { | 86 | pub(crate) mod function { |
| 83 | use crate::helper::{message, Action, Error, NextAction, Outcome, Result}; | 87 | use crate::helper::{message, Action, Context, Error, NextAction, Outcome, Result}; |
| 84 | use std::io::Read; | 88 | use std::io::Read; |
| 85 | use std::process::{Command, Stdio}; | 89 | use std::process::{Command, Stdio}; |
| 86 | 90 | ||
| @@ -125,7 +129,9 @@ pub(crate) mod function { · 38 unmodified lines | |||
| 125 | .transpose()?; | 129 | .transpose()?; |
| 126 | let status = child.wait()?; | 130 | let status = child.wait()?; |
| 127 | if !status.success() { | 131 | if !status.success() { |
| 128 | return Err(Error::CredentialsHelperFailed { code: status.code() }); | 132 | return Err(Error::CredentialsHelperFailed { |
| 133 | source: std::io::Error::new(std::io::ErrorKind::Other, "credentials helper failed"), | ||
| 134 | }); | ||
| 129 | } | 135 | } |
| 130 | 136 | ||
| 131 | match stdout { | 137 | match stdout { |
| @@ -150,7 +156,52 @@ pub(crate) mod function { · 18 unmodified lines | |||
| 150 | } | 156 | } |
| 151 | } | 157 | } |
| 152 | } | 158 | } |
| 159 | |||
| 160 | /// Invoke the given `helper` with `action` in `context`. | ||
| 161 | /// | ||
| 162 | /// Usually the first call is performed with [`Action::Fill`] to obtain an identity, which subsequently can be used. | ||
| 163 | /// On successful usage, use [`NextAction::approve()`], otherwise [`NextAction::reject()`]. | ||
| 164 | pub fn invoke(mut helper: impl crate::Helper, action: Action<'_>, _context: Context) -> Result { | ||
| 165 | let (stdin, stdout) = helper.start(&action)?; | ||
| 166 | action.send(stdin)?; | ||
| 167 | let stdout = stdout | ||
| 168 | .map(|mut stdout| { | ||
| 169 | let mut buf = Vec::new(); | ||
| 170 | stdout.read_to_end(&mut buf).map(|_| buf) | ||
| 171 | }) | ||
| 172 | .transpose()?; | ||
| 173 | helper.finish().map_err(|err| { | ||
| 174 | if err.kind() == std::io::ErrorKind::Other { | ||
| 175 | Error::CredentialsHelperFailed { source: err } | ||
| 176 | } else { | ||
| 177 | err.into() | ||
| 178 | } | ||
| 179 | })?; | ||
| 180 | |||
| 181 | match stdout { | ||
| 182 | None => Ok(None), | ||
| 183 | Some(stdout) => { | ||
| 184 | let kvs = message::decode(stdout.as_slice())?; | ||
| 185 | let find = |name: &str| { | ||
| 186 | kvs.iter() | ||
| 187 | .find(|(k, _)| k == name) | ||
| 188 | .ok_or_else(|| Error::KeyNotFound { name: name.into() }) | ||
| 189 | .map(|(_, n)| n.to_owned()) | ||
| 190 | }; | ||
| 191 | Ok(Some(Outcome { | ||
| 192 | identity: git_sec::identity::Account { | ||
| 193 | username: find("username")?, | ||
| 194 | password: find("password")?, | ||
| 195 | }, | ||
| 196 | next: NextAction { | ||
| 197 | previous_output: stdout.into(), | ||
| 198 | }, | ||
| 199 | })) | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } | ||
| 153 | } | 203 | } |
| 204 | pub use function::invoke; | ||
| 154 | 205 | ||
| 155 | /// | 206 | /// |
| 156 | pub mod message { | 207 | pub mod message { |