summaryrefslogtreecommitdiff
path: root/src/bin/git-autosave.rs
blob: 3df94b640d97d08863633cff6d3b0db7069ff77d (plain)
use std::path::Path;

use auth_git2::{GitAuthenticator, Prompter};
use git_autosave::{Config, commit_autosave, push_autosaves};
use git2::{RemoteCallbacks, Repository};
use inquire::{InquireError, PasswordDisplayMode};

#[derive(Default, Debug, Clone, PartialEq, Eq)]
struct Inquirer(Config);

fn config_value(git_config: &git2::Config, name: &str) -> Option<String> {
	git_config
		.get_entry(name)
		.ok()
		.and_then(|entry| entry.value().map(|entry| entry.to_string()))
}

fn prompt_secret(message: &str) -> Result<String, InquireError> {
	inquire::Password::new(message)
		.without_confirmation()
		.with_display_mode(PasswordDisplayMode::Masked)
		.prompt()
}

impl Prompter for Inquirer {
	fn prompt_username_password(
		&mut self,
		url: &str,
		git_config: &git2::Config,
	) -> Option<(String, String)> {
		let username = self
			.0
			.username_for_url(url)
			.cloned()
			.or_else(|| config_value(git_config, "autosave.username"));
		let password = self
			.0
			.password_for_url(url)
			.cloned()
			.or_else(|| config_value(git_config, "autosave.password"));
		if let Some(username) = username
			&& let Some(password) = password
		{
			return Some((username, password));
		}

		println!("Authenticating to {url}");
		let username = inquire::prompt_text("Username:").ok()?;
		let password = prompt_secret("Password:").ok()?;

		Some((username, password))
	}

	fn prompt_password(
		&mut self,
		username: &str,
		url: &str,
		git_config: &git2::Config,
	) -> Option<String> {
		let password = self
			.0
			.password_for_url(url)
			.cloned()
			.or_else(|| config_value(git_config, "autosave.password"));
		if let Some(password) = password {
			return Some(password);
		}

		println!("Authenticating to {url}");
		prompt_secret(&format!("Password for {username}:")).ok()
	}

	fn prompt_ssh_key_passphrase(
		&mut self,
		private_key_path: &Path,
		git_config: &git2::Config,
	) -> Option<String> {
		let password = self
			.0
			.passphrase_for_key(private_key_path)
			.cloned()
			.or_else(|| config_value(git_config, "autosave.password"));
		if let Some(password) = password {
			return Some(password);
		}

		prompt_secret(&format!(
			"Passphrase for {}:",
			private_key_path.to_string_lossy()
		))
		.ok()
	}
}

fn main() -> Result<(), anyhow::Error> {
	let repository = Repository::discover(".")?;
	let gitconfig = repository.config()?;
	let mut config = git_autosave::load_config()?;

	if std::env::args().any(|arg| arg == "--init") {
		git_autosave::init(&repository, Some(&mut config))?;
		git_autosave::save_config(&config)?;
	}

	let auth = GitAuthenticator::new().set_prompter(Inquirer(config));
	let mut callbacks = RemoteCallbacks::new();
	callbacks.credentials(auth.credentials(&gitconfig));

	commit_autosave(&repository)?;
	push_autosaves(&repository, callbacks)?;

	Ok(())
}