use radicle::cob::issue::Issue; use radicle::cob::thread::{Comment, CommentId}; use radicle::crypto::ssh::keystore::MemorySigner; use radicle::crypto::{ssh::Keystore, Signer}; use radicle::profile::env::RAD_PASSPHRASE; use radicle::profile::Profile; pub use radicle_term::io::*; pub use radicle_term::spinner; use inquire::validator; /// Validates secret key passphrases. #[derive(Clone)] pub struct PassphraseValidator { keystore: Keystore, } impl PassphraseValidator { /// Create a new validator. pub fn new(keystore: Keystore) -> Self { Self { keystore } } } impl inquire::validator::StringValidator for PassphraseValidator { fn validate( &self, input: &str, ) -> Result { let passphrase = Passphrase::from(input.to_owned()); if self.keystore.is_valid_passphrase(&passphrase)? { Ok(validator::Validation::Valid) } else { Ok(validator::Validation::Invalid( validator::ErrorMessage::from("Invalid passphrase, please try again"), )) } } } /// Get the signer. First we try getting it from ssh-agent, otherwise we prompt the user. pub fn signer(profile: &Profile) -> anyhow::Result> { if let Ok(signer) = profile.signer() { return Ok(signer); } let validator = PassphraseValidator::new(profile.keystore.clone()); let passphrase = passphrase(RAD_PASSPHRASE, validator)?; let spinner = spinner("Unsealing key..."); let signer = MemorySigner::load(&profile.keystore, Some(passphrase))?; spinner.finish(); Ok(signer.boxed()) } pub fn comment_select(issue: &Issue) -> Option<(&CommentId, &Comment)> { let comments = issue.comments().collect::>(); let selection = Select::new( "Which comment do you want to react to?", (0..comments.len()).collect(), ) .with_render_config(*CONFIG) .with_formatter(&|i| comments[i.index].1.body().to_owned()) .prompt() .ok()?; comments.get(selection).copied() }