Skip to main content

git_lfs_creds/
helper.rs

1//! The `Helper` trait and shared types.
2
3use crate::query::Query;
4
5/// A username/password pair returned by a credential helper.
6///
7/// Username may be empty: some servers accept a token-as-password with no
8/// username set (e.g. GitHub personal access tokens). Password is
9/// required; helpers that can't supply one should return `Ok(None)`.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Credentials {
12    /// Username string. May be empty for token-as-password setups.
13    pub username: String,
14    /// Password (or token) string.
15    pub password: String,
16}
17
18impl Credentials {
19    /// Build a credentials pair from any pair of string-like values.
20    pub fn new(username: impl Into<String>, password: impl Into<String>) -> Self {
21        Self {
22            username: username.into(),
23            password: password.into(),
24        }
25    }
26}
27
28/// Things that can go wrong while invoking a credential helper.
29#[derive(Debug, thiserror::Error)]
30pub enum HelperError {
31    /// Failed to spawn or talk to the helper subprocess.
32    #[error("io error invoking credential helper: {0}")]
33    Io(#[from] std::io::Error),
34    /// The helper ran but reported a failure (non-zero exit, malformed
35    /// stdout, refused input, etc).
36    #[error("credential helper failed: {0}")]
37    Failed(String),
38}
39
40/// Resolve credentials for a given query, and report success/failure back
41/// so the helper can persist or invalidate its state.
42///
43/// `Send + Sync` so a single helper can be shared across the async
44/// transfer queue. Implementations that aren't naturally thread-safe
45/// (e.g. wrap a `Cell`) should layer their own synchronization.
46pub trait Helper: Send + Sync {
47    /// Try to fetch credentials for `query`.
48    ///
49    /// `Ok(None)` means "I don't know"; the chain should consult the
50    /// next helper. `Err` is a hard failure (e.g. helper subprocess
51    /// crashed) and aborts the chain.
52    fn fill(&self, query: &Query) -> Result<Option<Credentials>, HelperError>;
53
54    /// Tell the helper that `creds` worked for `query`.
55    ///
56    /// Helpers that can persist (git credential, OS keychain via
57    /// `git credential`) should store the pair; pure caches use this
58    /// to populate themselves.
59    fn approve(&self, query: &Query, creds: &Credentials) -> Result<(), HelperError>;
60
61    /// Tell the helper that `creds` did **not** work for `query`.
62    ///
63    /// Helpers should drop the credentials so we don't loop on stale
64    /// entries.
65    fn reject(&self, query: &Query, creds: &Credentials) -> Result<(), HelperError>;
66}