From 4176a055fb7c93351b7d094589cfa48b91f98fb3 Mon Sep 17 00:00:00 2001 From: a-moreira Date: Sat, 24 Dec 2022 20:19:49 -0300 Subject: [PATCH 01/11] first try with dialoguer crate --- cli/Cargo.toml | 2 +- cli/src/main.rs | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 094f0c5a..f00a6837 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -19,7 +19,7 @@ clap_complete = "3.1.4" env_logger = "0.10.0" console = "0.15.0" indicatif = "0.17.0" -dialoguer = "0.10.0" +dialoguer = { version = "0.10", features = ["completion"] } color-eyre = "0.6.0" number_prefix = "0.4.0" ctrlc = "3.2.1" diff --git a/cli/src/main.rs b/cli/src/main.rs index e7d07b1a..f7998bca 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -702,13 +702,49 @@ fn create_progress_bar(file_size: u64) -> ProgressBar { pb } +struct TabCompletion { + options: Vec +} + +impl Default for TabCompletion { + fn default() -> Self { + TabCompletion { + options: vec![ + "baboon".to_string(), + "belowground".to_string() + ] + } + } +} + +use dialoguer::Completion; + +impl Completion for TabCompletion { + fn get(&self, input: &str) -> Option { + let matches = self + .options + .iter() + .filter(|option| option.starts_with(input)) + .collect::>(); + + if matches.len() == 1 { + Some(matches[0].to_string()) + } else { + None + } + } +} + fn enter_code() -> eyre::Result { use dialoguer::Input; - Input::new() + let completion = TabCompletion::default(); + let input = Input::new() .with_prompt("Enter code") + .completion_with(&completion) .interact_text() - .map_err(From::from) + .map_err(From::from); + input } fn print_welcome(term: &mut Term, welcome: &magic_wormhole::WormholeWelcome) -> eyre::Result<()> { From 83e028525911b6f7f7418bebecc8a4bd52153d13 Mon Sep 17 00:00:00 2001 From: a-moreira Date: Mon, 26 Dec 2022 16:01:35 -0300 Subject: [PATCH 02/11] feat: add code completion --- cli/src/main.rs | 52 +++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index f7998bca..ae5db89a 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -3,7 +3,7 @@ mod util; use std::{ ops::Deref, - time::{Duration, Instant}, + time::{Duration, Instant}, fs, }; use async_std::{fs::OpenOptions, sync::Arc}; @@ -11,13 +11,14 @@ use clap::{Args, CommandFactory, Parser, Subcommand}; use cli_clipboard::{ClipboardContext, ClipboardProvider}; use color_eyre::{eyre, eyre::Context}; use console::{style, Term}; +use dialoguer::{Input, Completion}; use futures::{future::Either, Future, FutureExt}; use indicatif::{MultiProgress, ProgressBar}; use std::{ io::Write, path::{Path, PathBuf}, }; - +use std::collections::HashMap; use magic_wormhole::{forwarding, transfer, transit, Wormhole}; fn install_ctrlc_handler( @@ -702,29 +703,43 @@ fn create_progress_bar(file_size: u64) -> ProgressBar { pb } -struct TabCompletion { - options: Vec +struct PgpWordList { + even_words: Vec, + odd_words: Vec } -impl Default for TabCompletion { +impl Default for PgpWordList { fn default() -> Self { - TabCompletion { - options: vec![ - "baboon".to_string(), - "belowground".to_string() - ] + let json = fs::read_to_string("./src/core/pgpwords.json").unwrap(); + let word_map: HashMap> = serde_json::from_str(&json).unwrap(); + let mut even_words: Vec = vec![]; + let mut odd_words: Vec = vec![]; + for (_idx, words) in word_map { + even_words.push(words[0].to_lowercase()); + odd_words.push(words[1].to_lowercase()); + } + + PgpWordList { + even_words, + odd_words } } } -use dialoguer::Completion; - -impl Completion for TabCompletion { +impl Completion for PgpWordList { fn get(&self, input: &str) -> Option { - let matches = self - .options + let count = input.matches("-").count(); + let word_list: Vec; + // we start with the odd words + if count % 2 == 0 { + word_list = self.odd_words.clone(); + } else { + word_list = self.even_words.clone(); + } + + let matches = word_list .iter() - .filter(|option| option.starts_with(input)) + .filter(|word| word.starts_with(input)) .collect::>(); if matches.len() == 1 { @@ -736,14 +751,13 @@ impl Completion for TabCompletion { } fn enter_code() -> eyre::Result { - use dialoguer::Input; - - let completion = TabCompletion::default(); + let completion = PgpWordList::default(); let input = Input::new() .with_prompt("Enter code") .completion_with(&completion) .interact_text() .map_err(From::from); + input } From b737838023607a6c070ee2b6c937a84ca54b5df2 Mon Sep 17 00:00:00 2001 From: a-moreira Date: Mon, 26 Dec 2022 16:03:48 -0300 Subject: [PATCH 03/11] cargo fmt --- cli/src/main.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index ae5db89a..773c9af9 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -2,8 +2,9 @@ mod util; use std::{ + fs, ops::Deref, - time::{Duration, Instant}, fs, + time::{Duration, Instant}, }; use async_std::{fs::OpenOptions, sync::Arc}; @@ -11,15 +12,15 @@ use clap::{Args, CommandFactory, Parser, Subcommand}; use cli_clipboard::{ClipboardContext, ClipboardProvider}; use color_eyre::{eyre, eyre::Context}; use console::{style, Term}; -use dialoguer::{Input, Completion}; +use dialoguer::{Completion, Input}; use futures::{future::Either, Future, FutureExt}; use indicatif::{MultiProgress, ProgressBar}; +use magic_wormhole::{forwarding, transfer, transit, Wormhole}; use std::{ + collections::HashMap, io::Write, path::{Path, PathBuf}, }; -use std::collections::HashMap; -use magic_wormhole::{forwarding, transfer, transit, Wormhole}; fn install_ctrlc_handler( ) -> eyre::Result futures::future::BoxFuture<'static, ()> + Clone> { @@ -705,7 +706,7 @@ fn create_progress_bar(file_size: u64) -> ProgressBar { struct PgpWordList { even_words: Vec, - odd_words: Vec + odd_words: Vec, } impl Default for PgpWordList { @@ -721,7 +722,7 @@ impl Default for PgpWordList { PgpWordList { even_words, - odd_words + odd_words, } } } @@ -729,13 +730,12 @@ impl Default for PgpWordList { impl Completion for PgpWordList { fn get(&self, input: &str) -> Option { let count = input.matches("-").count(); - let word_list: Vec; // we start with the odd words - if count % 2 == 0 { - word_list = self.odd_words.clone(); + let word_list: Vec = if count % 2 == 0 { + self.odd_words.clone() } else { - word_list = self.even_words.clone(); - } + self.even_words.clone() + }; let matches = word_list .iter() From 528dac825ae37caad6a1294e4672d5278ec5d64f Mon Sep 17 00:00:00 2001 From: a-moreira Date: Thu, 6 Jul 2023 13:39:59 -0300 Subject: [PATCH 04/11] re-use some code --- Cargo.lock | 1 + Cargo.toml | 1 + cli/src/main.rs | 47 +----------------------- src/core.rs | 2 +- src/core/wordlist.rs | 85 +++++++++++++++++++++++++++----------------- src/lib.rs | 4 ++- 6 files changed, 59 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c05e8bb1..2a979b15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1322,6 +1322,7 @@ dependencies = [ "base64 0.20.0", "bytecodec", "derive_more", + "dialoguer", "env_logger", "eyre", "futures", diff --git a/Cargo.toml b/Cargo.toml index 43869809..759b6043 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ edition = "2021" rust-version = "1.61" # MSRV (also change in CI) [dependencies] +dialoguer = { version = "0.10", features = ["completion"] } serde = { version = "1.0.120", features = ["rc"] } serde_json = "1.0.61" serde_derive = "1.0.120" diff --git a/cli/src/main.rs b/cli/src/main.rs index 773c9af9..612eb60b 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -15,6 +15,7 @@ use console::{style, Term}; use dialoguer::{Completion, Input}; use futures::{future::Either, Future, FutureExt}; use indicatif::{MultiProgress, ProgressBar}; +use magic_wormhole::PgpWordList; use magic_wormhole::{forwarding, transfer, transit, Wormhole}; use std::{ collections::HashMap, @@ -704,52 +705,6 @@ fn create_progress_bar(file_size: u64) -> ProgressBar { pb } -struct PgpWordList { - even_words: Vec, - odd_words: Vec, -} - -impl Default for PgpWordList { - fn default() -> Self { - let json = fs::read_to_string("./src/core/pgpwords.json").unwrap(); - let word_map: HashMap> = serde_json::from_str(&json).unwrap(); - let mut even_words: Vec = vec![]; - let mut odd_words: Vec = vec![]; - for (_idx, words) in word_map { - even_words.push(words[0].to_lowercase()); - odd_words.push(words[1].to_lowercase()); - } - - PgpWordList { - even_words, - odd_words, - } - } -} - -impl Completion for PgpWordList { - fn get(&self, input: &str) -> Option { - let count = input.matches("-").count(); - // we start with the odd words - let word_list: Vec = if count % 2 == 0 { - self.odd_words.clone() - } else { - self.even_words.clone() - }; - - let matches = word_list - .iter() - .filter(|word| word.starts_with(input)) - .collect::>(); - - if matches.len() == 1 { - Some(matches[0].to_string()) - } else { - None - } - } -} - fn enter_code() -> eyre::Result { let completion = PgpWordList::default(); let input = Input::new() diff --git a/src/core.rs b/src/core.rs index 27bf0d3c..dac9c9df 100644 --- a/src/core.rs +++ b/src/core.rs @@ -3,7 +3,7 @@ pub mod rendezvous; mod server_messages; #[cfg(test)] mod test; -mod wordlist; +pub mod wordlist; use serde_derive::{Deserialize, Serialize}; use std::borrow::Cow; diff --git a/src/core/wordlist.rs b/src/core/wordlist.rs index 01cc1f42..940d374a 100644 --- a/src/core/wordlist.rs +++ b/src/core/wordlist.rs @@ -2,31 +2,56 @@ use rand::{rngs::OsRng, seq::SliceRandom}; use serde_json::{self, Value}; use std::fmt; -#[derive(PartialEq)] -pub struct Wordlist { - pub num_words: usize, +use dialoguer::Completion; +use std::collections::HashMap; +use std::fs; + +pub struct PgpWordList { words: Vec>, + num_words: usize, } -impl fmt::Debug for Wordlist { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Wordlist ( {}, lots of words...)", self.num_words) +impl PgpWordList { + pub fn choose_words(&self) -> String { + let mut rng = OsRng; + let components: Vec; + components = self + .words + .iter() + .cycle() + .take(self.num_words) + .map(|words| words.choose(&mut rng).unwrap().to_string()) + .collect(); + components.join("-") } } -impl Wordlist { - #[cfg(test)] - pub fn new(num_words: usize, words: Vec>) -> Wordlist { - Wordlist { num_words, words } +impl Default for PgpWordList { + fn default() -> Self { + let json = fs::read_to_string("./src/core/pgpwords.json").unwrap(); + let word_map: HashMap> = serde_json::from_str(&json).unwrap(); + let mut even_words: Vec = vec![]; + let mut odd_words: Vec = vec![]; + for (_idx, words) in word_map { + even_words.push(words[0].to_lowercase()); + odd_words.push(words[1].to_lowercase()); + } + let words = vec![even_words, odd_words]; + + PgpWordList { + words: words.clone(), + num_words: words.len(), + } } +} - #[allow(dead_code)] // TODO make this API public one day - pub fn get_completions(&self, prefix: &str) -> Vec { - let count_dashes = prefix.matches('-').count(); +impl Completion for PgpWordList { + fn get(&self, input: &str) -> Option { + let count_dashes = input.matches('-').count(); let mut completions = Vec::new(); let words = &self.words[count_dashes % self.words.len()]; - let last_partial_word = prefix.split('-').last(); + let last_partial_word = input.split('-').last(); let lp = if let Some(w) = last_partial_word { w.len() } else { @@ -34,12 +59,12 @@ impl Wordlist { }; for word in words { - let mut suffix: String = prefix.to_owned(); + let mut suffix: String = input.to_owned(); if word.starts_with(last_partial_word.unwrap()) { if lp == 0 { suffix.push_str(&word); } else { - let p = prefix.len() - lp; + let p = input.len() - lp; suffix.truncate(p as usize); suffix.push_str(&word); } @@ -51,21 +76,15 @@ impl Wordlist { completions.push(suffix); } } - completions.sort(); - completions - } - - pub fn choose_words(&self) -> String { - let mut rng = OsRng; - let components: Vec; - components = self - .words - .iter() - .cycle() - .take(self.num_words) - .map(|words| words.choose(&mut rng).unwrap().to_string()) - .collect(); - components.join("-") + if completions.len() == 1 { + Some(completions.first().unwrap().clone()) + } else if completions.len() == 0 { + None + } else { + // TODO: show vector of suggestions somehow + // println!("Suggestions: {:#?}", &completions); + None + } } } @@ -97,8 +116,8 @@ fn load_pgpwords() -> Vec> { vec![even_words, odd_words] } -pub fn default_wordlist(num_words: usize) -> Wordlist { - Wordlist { +pub fn default_wordlist(num_words: usize) -> PgpWordList { + PgpWordList { num_words, words: load_pgpwords(), } diff --git a/src/lib.rs b/src/lib.rs index 937b43c5..6b779fca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,5 +37,7 @@ pub mod uri; pub use crate::core::{ key::{GenericKey, Key, KeyPurpose, WormholeKey}, - rendezvous, AppConfig, AppID, Code, Wormhole, WormholeError, WormholeWelcome, + rendezvous, + wordlist::PgpWordList, + AppConfig, AppID, Code, Wormhole, WormholeError, WormholeWelcome, }; From 7dc9c4385e137d7438bce4515bf1f5e199206988 Mon Sep 17 00:00:00 2001 From: a-moreira Date: Thu, 6 Jul 2023 13:41:15 -0300 Subject: [PATCH 05/11] cargo clippy --- cli/src/main.rs | 10 ++++------ cli/src/util.rs | 2 +- src/core.rs | 8 ++++---- src/core/key.rs | 6 +++--- src/core/wordlist.rs | 16 ++++++++-------- src/transit/crypto.rs | 8 ++++---- src/uri.rs | 4 ++-- src/util.rs | 2 +- 8 files changed, 27 insertions(+), 29 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 612eb60b..0f3fa519 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -2,7 +2,6 @@ mod util; use std::{ - fs, ops::Deref, time::{Duration, Instant}, }; @@ -12,13 +11,12 @@ use clap::{Args, CommandFactory, Parser, Subcommand}; use cli_clipboard::{ClipboardContext, ClipboardProvider}; use color_eyre::{eyre, eyre::Context}; use console::{style, Term}; -use dialoguer::{Completion, Input}; +use dialoguer::{Input}; use futures::{future::Either, Future, FutureExt}; use indicatif::{MultiProgress, ProgressBar}; use magic_wormhole::PgpWordList; use magic_wormhole::{forwarding, transfer, transit, Wormhole}; use std::{ - collections::HashMap, io::Write, path::{Path, PathBuf}, }; @@ -577,7 +575,7 @@ async fn main() -> eyre::Result<()> { out, ); - std::io::stdout().write_all(&out.as_bytes())?; + std::io::stdout().write_all(out.as_bytes())?; }, shell => { let mut out = std::io::stdout(); @@ -1023,7 +1021,7 @@ async fn receive( .truncate(true) .open(&file_path) .await?; - Ok(req + req .accept( &transit::log_transit_connection, on_progress, @@ -1031,7 +1029,7 @@ async fn receive( ctrl_c(), ) .await - .context("Receive process failed")?) + .context("Receive process failed") } #[cfg(test)] diff --git a/cli/src/util.rs b/cli/src/util.rs index 51d517ea..d76450bf 100644 --- a/cli/src/util.rs +++ b/cli/src/util.rs @@ -20,7 +20,7 @@ pub async fn ask_user(message: impl std::fmt::Display, default_answer: bool) -> let mut answer = String::new(); stdin.read_line(&mut answer).await.unwrap(); - match &*answer.to_lowercase().trim() { + match answer.to_lowercase().trim() { "y" | "yes" => break true, "n" | "no" => break false, "" => break default_answer, diff --git a/src/core.rs b/src/core.rs index dac9c9df..9b55be04 100644 --- a/src/core.rs +++ b/src/core.rs @@ -516,9 +516,9 @@ impl Nameplate { } } -impl Into for Nameplate { - fn into(self) -> String { - self.0 +impl From for String { + fn from(val: Nameplate) -> Self { + val.0 } } @@ -545,6 +545,6 @@ impl Code { } pub fn nameplate(&self) -> Nameplate { - Nameplate::new(self.0.splitn(2, '-').next().unwrap()) + Nameplate::new(self.0.split('-').next().unwrap()) } } diff --git a/src/core/key.rs b/src/core/key.rs index eb3dab7b..c6b316ec 100644 --- a/src/core/key.rs +++ b/src/core/key.rs @@ -49,7 +49,7 @@ impl Key { */ #[cfg(feature = "transit")] pub fn derive_transit_key(&self, appid: &AppID) -> Key { - let transit_purpose = format!("{}/transit-key", &*appid); + let transit_purpose = format!("{}/transit-key", appid); let derived_key = self.derive_subkey_from_purpose(&transit_purpose); trace!( @@ -68,7 +68,7 @@ impl Key

{ } pub fn to_hex(&self) -> String { - hex::encode(&**self) + hex::encode(**self) } /** @@ -76,7 +76,7 @@ impl Key

{ */ pub fn derive_subkey_from_purpose(&self, purpose: &str) -> Key { Key( - Box::new(derive_key(&*self, purpose.as_bytes())), + Box::new(derive_key(self, purpose.as_bytes())), std::marker::PhantomData, ) } diff --git a/src/core/wordlist.rs b/src/core/wordlist.rs index 940d374a..485de8d5 100644 --- a/src/core/wordlist.rs +++ b/src/core/wordlist.rs @@ -1,6 +1,6 @@ use rand::{rngs::OsRng, seq::SliceRandom}; use serde_json::{self, Value}; -use std::fmt; + use dialoguer::Completion; use std::collections::HashMap; @@ -14,8 +14,8 @@ pub struct PgpWordList { impl PgpWordList { pub fn choose_words(&self) -> String { let mut rng = OsRng; - let components: Vec; - components = self + + let components: Vec = self .words .iter() .cycle() @@ -62,15 +62,15 @@ impl Completion for PgpWordList { let mut suffix: String = input.to_owned(); if word.starts_with(last_partial_word.unwrap()) { if lp == 0 { - suffix.push_str(&word); + suffix.push_str(word); } else { let p = input.len() - lp; - suffix.truncate(p as usize); - suffix.push_str(&word); + suffix.truncate(p); + suffix.push_str(word); } if count_dashes + 1 < self.num_words { - suffix.push_str("-"); + suffix.push('-'); } completions.push(suffix); @@ -78,7 +78,7 @@ impl Completion for PgpWordList { } if completions.len() == 1 { Some(completions.first().unwrap().clone()) - } else if completions.len() == 0 { + } else if completions.is_empty() { None } else { // TODO: show vector of suggestions somehow diff --git a/src/transit/crypto.rs b/src/transit/crypto.rs index 6cda14c8..0bf2975a 100644 --- a/src/transit/crypto.rs +++ b/src/transit/crypto.rs @@ -293,7 +293,7 @@ impl TransitCryptoInit for NoiseInit { builder.set_is_initiator(true); builder.build_handshake_state() }; - handshake.push_psk(&*self.key); + handshake.push_psk(&self.key); // → psk, e write_transit_message(socket, &handshake.write_message_vec(&[])?).await?; @@ -307,7 +307,7 @@ impl TransitCryptoInit for NoiseInit { // ← "" let peer_confirmation_message = rx.decrypt_vec(&read_transit_message(socket).await?)?; ensure!( - peer_confirmation_message.len() == 0, + peer_confirmation_message.is_empty(), TransitHandshakeError::HandshakeFailed ); @@ -354,7 +354,7 @@ impl TransitCryptoInit for NoiseInit { builder.set_is_initiator(false); builder.build_handshake_state() }; - handshake.push_psk(&*self.key); + handshake.push_psk(&self.key); // ← psk, e handshake.read_message(&read_transit_message(socket).await?, &mut [])?; @@ -372,7 +372,7 @@ impl TransitCryptoInit for NoiseInit { // ← "" let peer_confirmation_message = rx.decrypt_vec(&read_transit_message(socket).await?)?; ensure!( - peer_confirmation_message.len() == 0, + peer_confirmation_message.is_empty(), TransitHandshakeError::HandshakeFailed ); diff --git a/src/uri.rs b/src/uri.rs index 7713700e..e071401d 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -125,13 +125,13 @@ impl std::str::FromStr for WormholeTransferUri { impl From<&WormholeTransferUri> for url::Url { fn from(val: &WormholeTransferUri) -> Self { let mut url = url::Url::parse("wormhole-transfer:").unwrap(); - url.set_path(&*val.code); + url.set_path(&val.code); /* Only do this if there are any query parameteres at all, otherwise the URL will have an ugly trailing '?'. */ if val.rendezvous_server.is_some() || val.is_leader { let mut query = url.query_pairs_mut(); query.clear(); if let Some(rendezvous_server) = val.rendezvous_server.as_ref() { - query.append_pair("rendezvous", &rendezvous_server.to_string()); + query.append_pair("rendezvous", rendezvous_server.as_ref()); } if val.is_leader { query.append_pair("role", "leader"); diff --git a/src/util.rs b/src/util.rs index dc7148b6..84438a27 100644 --- a/src/util.rs +++ b/src/util.rs @@ -19,7 +19,7 @@ pub struct DisplayBytes<'a>(pub &'a [u8]); impl std::fmt::Display for DisplayBytes<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let hex_decode = hex::decode(&self.0); + let hex_decode = hex::decode(self.0); let (string, hex_param) = match hex_decode.as_ref().map(Vec::as_slice) { Ok(decoded_hex) => (decoded_hex, "hex-encoded "), Err(_) => (self.0, ""), From 103bb2482b32c9271f48af6bce5db9487d5b17a9 Mon Sep 17 00:00:00 2001 From: a-moreira Date: Thu, 6 Jul 2023 15:39:15 -0300 Subject: [PATCH 06/11] fix CI --- cli/src/main.rs | 22 +++++++++------------- src/core.rs | 2 +- src/core/wordlist.rs | 5 ++--- src/lib.rs | 4 ++-- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index b5740c3d..8406396d 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -8,18 +8,15 @@ use clap::{Args, CommandFactory, Parser, Subcommand}; use cli_clipboard::{ClipboardContext, ClipboardProvider}; use color_eyre::{eyre, eyre::Context}; use console::{style, Term}; -use dialoguer::{Input}; +use dialoguer::Input; use futures::{future::Either, Future, FutureExt}; use indicatif::{MultiProgress, ProgressBar}; use magic_wormhole::PgpWordList; -use magic_wormhole::{forwarding, transfer, transit, Wormhole}; use std::{ io::Write, path::{Path, PathBuf}, }; -use std::{io::Write, path::PathBuf}; - use magic_wormhole::{ dilated_transfer, forwarding, transfer, transit, MailboxConnection, Wormhole, }; @@ -1069,15 +1066,14 @@ async fn receive_inner_v1( .truncate(true) .open(&file_path) .await?; - req - .accept( - &transit::log_transit_connection, - &mut file, - create_progress_handler(pb), - ctrl_c(), - ) - .await - .context("Receive process failed") + req.accept( + &transit::log_transit_connection, + &mut file, + create_progress_handler(pb), + ctrl_c(), + ) + .await + .context("Receive process failed") } async fn receive_inner_v2( diff --git a/src/core.rs b/src/core.rs index 7b50900a..e7e9aca1 100644 --- a/src/core.rs +++ b/src/core.rs @@ -18,7 +18,7 @@ pub mod rendezvous; mod server_messages; #[cfg(test)] pub(crate) mod test; -mod wordlist; +pub mod wordlist; #[derive(Debug, thiserror::Error)] #[non_exhaustive] diff --git a/src/core/wordlist.rs b/src/core/wordlist.rs index 2a7531ab..ffbe4dfa 100644 --- a/src/core/wordlist.rs +++ b/src/core/wordlist.rs @@ -3,8 +3,7 @@ use serde_json::{self, Value}; use std::fmt; use dialoguer::Completion; -use std::collections::HashMap; -use std::fs; +use std::{collections::HashMap, fs}; pub struct PgpWordList { words: Vec>, @@ -14,7 +13,7 @@ pub struct PgpWordList { impl PgpWordList { pub fn choose_words(&self) -> String { let mut rng = OsRng; - + let components: Vec = self .words .iter() diff --git a/src/lib.rs b/src/lib.rs index 71441d51..3ad168e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,9 +41,9 @@ pub mod uri; pub use crate::core::{ key::{GenericKey, Key, KeyPurpose, WormholeKey}, + rendezvous, wordlist::PgpWordList, - rendezvous, AppConfig, AppID, Code, MailboxConnection, Mood, Nameplate, Wormhole, - WormholeError, + AppConfig, AppID, Code, MailboxConnection, Mood, Nameplate, Wormhole, WormholeError, }; #[cfg(feature = "dilation")] From 7f1b8a7dbefbcd00df3d12fc6628e828a962d23f Mon Sep 17 00:00:00 2001 From: a-moreira Date: Thu, 6 Jul 2023 15:53:00 -0300 Subject: [PATCH 07/11] fix wordlist.rs tests --- src/core/wordlist.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/wordlist.rs b/src/core/wordlist.rs index ffbe4dfa..2d354867 100644 --- a/src/core/wordlist.rs +++ b/src/core/wordlist.rs @@ -167,10 +167,10 @@ mod test { ]; let w = Wordlist::new(2, words); - assert_eq!(w.get_completions(""), vec!["green-", "purple-", "yellow-"]); - assert_eq!(w.get_completions("pur"), vec!["purple-"]); - assert_eq!(w.get_completions("blu"), Vec::::new()); - assert_eq!(w.get_completions("purple-sa"), vec!["purple-sausages"]); + assert_eq!(w.get(""), vec!["green-", "purple-", "yellow-"]); + assert_eq!(w.get("pur"), vec!["purple-"]); + assert_eq!(w.get("blu"), Vec::::new()); + assert_eq!(w.get("purple-sa"), vec!["purple-sausages"]); } #[test] From 2665131b25e60e63f5c48a4c7c6e527d8e423e32 Mon Sep 17 00:00:00 2001 From: a-moreira Date: Fri, 7 Jul 2023 10:21:19 -0300 Subject: [PATCH 08/11] split wordlist functionalities between crate and cli --- cli/src/main.rs | 90 +++++++++++++++++++++- src/core/wordlist.rs | 174 ++++++------------------------------------- src/lib.rs | 2 +- 3 files changed, 112 insertions(+), 154 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 8406396d..081a5d8c 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -766,7 +766,7 @@ fn create_progress_handler(pb: ProgressBar) -> impl FnMut(u64, u64) { } fn enter_code() -> eyre::Result { - let completion = PgpWordList::default(); + let completion = WordList::default(); let input = Input::new() .with_prompt("Enter code") .completion_with(&completion) @@ -1178,6 +1178,77 @@ async fn receive_inner_v2( Ok(()) } +use dialoguer::Completion; +use magic_wormhole::core::wordlist; +use std::collections::HashMap; +use std::fs; + +struct WordList(PgpWordList); + +impl Default for WordList { + fn default() -> Self { + let json = fs::read_to_string("./src/core/pgpwords.json").unwrap(); + let word_map: HashMap> = serde_json::from_str(&json).unwrap(); + let mut even_words: Vec = vec![]; + let mut odd_words: Vec = vec![]; + for (_idx, words) in word_map { + even_words.push(words[0].to_lowercase()); + odd_words.push(words[1].to_lowercase()); + } + let words = vec![even_words, odd_words]; + + WordList { + 0: PgpWordList { + words: words.clone(), + num_words: words.len(), + }, + } + } +} + +impl Completion for WordList { + fn get(&self, input: &str) -> Option { + let count_dashes = input.matches('-').count(); + let mut completions = Vec::new(); + let words = &self.0.words[count_dashes % self.0.words.len()]; + + let last_partial_word = input.split('-').last(); + let lp = if let Some(w) = last_partial_word { + w.len() + } else { + 0 + }; + + for word in words { + let mut suffix: String = input.to_owned(); + if word.starts_with(last_partial_word.unwrap()) { + if lp == 0 { + suffix.push_str(word); + } else { + let p = input.len() - lp; + suffix.truncate(p); + suffix.push_str(word); + } + + if count_dashes + 1 < self.0.num_words { + suffix.push('-'); + } + + completions.push(suffix); + } + } + if completions.len() == 1 { + Some(completions.first().unwrap().clone()) + } else if completions.is_empty() { + None + } else { + // TODO: show vector of suggestions somehow + // println!("Suggestions: {:#?}", &completions); + None + } + } +} + #[cfg(test)] mod test { use super::*; @@ -1195,4 +1266,21 @@ mod test { String::from_utf8(out).unwrap(); } } + + #[test] + fn test_passphrase_completion() { + let words: Vec> = vec![ + wordlist::vecstrings("purple green yellow"), + wordlist::vecstrings("sausages seltzer snobol"), + ]; + + let w = WordList(PgpWordList { + words, + num_words: 2, + }); + assert_eq!(w.get(""), None); + assert_eq!(w.get("pur").unwrap(), "purple-"); + assert_eq!(w.get("blu"), None); + assert_eq!(w.get("purple-sa").unwrap(), "purple-sausages"); + } } diff --git a/src/core/wordlist.rs b/src/core/wordlist.rs index 2d354867..30cf4dbb 100644 --- a/src/core/wordlist.rs +++ b/src/core/wordlist.rs @@ -1,16 +1,15 @@ use rand::{rngs::OsRng, seq::SliceRandom}; use serde_json::{self, Value}; -use std::fmt; - -use dialoguer::Completion; -use std::{collections::HashMap, fs}; pub struct PgpWordList { - words: Vec>, - num_words: usize, + pub words: Vec>, + pub num_words: usize, } impl PgpWordList { + pub fn new(num_words: usize, words: Vec>) -> Self { + Self { words, num_words } + } pub fn choose_words(&self) -> String { let mut rng = OsRng; @@ -25,68 +24,6 @@ impl PgpWordList { } } -impl Default for PgpWordList { - fn default() -> Self { - let json = fs::read_to_string("./src/core/pgpwords.json").unwrap(); - let word_map: HashMap> = serde_json::from_str(&json).unwrap(); - let mut even_words: Vec = vec![]; - let mut odd_words: Vec = vec![]; - for (_idx, words) in word_map { - even_words.push(words[0].to_lowercase()); - odd_words.push(words[1].to_lowercase()); - } - let words = vec![even_words, odd_words]; - - PgpWordList { - words: words.clone(), - num_words: words.len(), - } - } -} - -impl Completion for PgpWordList { - fn get(&self, input: &str) -> Option { - let count_dashes = input.matches('-').count(); - let mut completions = Vec::new(); - let words = &self.words[count_dashes % self.words.len()]; - - let last_partial_word = input.split('-').last(); - let lp = if let Some(w) = last_partial_word { - w.len() - } else { - 0 - }; - - for word in words { - let mut suffix: String = input.to_owned(); - if word.starts_with(last_partial_word.unwrap()) { - if lp == 0 { - suffix.push_str(word); - } else { - let p = input.len() - lp; - suffix.truncate(p); - suffix.push_str(word); - } - - if count_dashes + 1 < self.num_words { - suffix.push('-'); - } - - completions.push(suffix); - } - } - if completions.len() == 1 { - Some(completions.first().unwrap().clone()) - } else if completions.is_empty() { - None - } else { - // TODO: show vector of suggestions somehow - // println!("Suggestions: {:#?}", &completions); - None - } - } -} - fn load_pgpwords() -> Vec> { let raw_words_value: serde_json::Value = serde_json::from_str(include_str!("pgpwords.json")).unwrap(); @@ -123,6 +60,18 @@ pub fn default_wordlist(num_words: usize) -> PgpWordList { } } +pub fn vecstrings(all: &str) -> Vec { + all.split_whitespace() + .map(|s| { + if s == "." { + String::from("") + } else { + s.to_string() + } + }) + .collect() +} + #[cfg(test)] mod test { use super::*; @@ -137,51 +86,15 @@ mod test { assert_eq!(w[1][255], "zulu"); } - #[test] - fn test_default_wordlist() { - let d = default_wordlist(2); - assert_eq!(d.words.len(), 2); - assert_eq!(d.words[0][0], "adroitness"); - assert_eq!(d.words[1][0], "aardvark"); - assert_eq!(d.words[0][255], "yucatan"); - assert_eq!(d.words[1][255], "zulu"); - } - - fn vecstrings(all: &str) -> Vec { - all.split_whitespace() - .map(|s| { - if s == "." { - String::from("") - } else { - s.to_string() - } - }) - .collect() - } - - #[test] - fn test_completion() { - let words: Vec> = vec![ - vecstrings("purple green yellow"), - vecstrings("sausages seltzer snobol"), - ]; - - let w = Wordlist::new(2, words); - assert_eq!(w.get(""), vec!["green-", "purple-", "yellow-"]); - assert_eq!(w.get("pur"), vec!["purple-"]); - assert_eq!(w.get("blu"), Vec::::new()); - assert_eq!(w.get("purple-sa"), vec!["purple-sausages"]); - } - #[test] fn test_choose_words() { let few_words: Vec> = vec![vecstrings("purple"), vecstrings("sausages")]; - let w = Wordlist::new(2, few_words.clone()); + let w = PgpWordList::new(2, few_words.clone()); assert_eq!(w.choose_words(), "purple-sausages"); - let w = Wordlist::new(3, few_words.clone()); + let w = PgpWordList::new(3, few_words.clone()); assert_eq!(w.choose_words(), "purple-sausages-purple"); - let w = Wordlist::new(4, few_words); + let w = PgpWordList::new(4, few_words); assert_eq!(w.choose_words(), "purple-sausages-purple-sausages"); } @@ -201,57 +114,14 @@ mod test { .map(|s| s.to_string()) .collect(); - let w = Wordlist::new(2, more_words.clone()); + let w = PgpWordList::new(2, more_words.clone()); for _ in 0..20 { assert!(expected2.contains(&w.choose_words())); } - let w = Wordlist::new(3, more_words); + let w = PgpWordList::new(3, more_words); for _ in 0..20 { assert!(expected3.contains(&w.choose_words())); } } - - #[test] - fn test_default_completions() { - let w = default_wordlist(2); - let c = w.get_completions("ar"); - assert_eq!(c.len(), 2); - assert!(c.contains(&String::from("article-"))); - assert!(c.contains(&String::from("armistice-"))); - - let c = w.get_completions("armis"); - assert_eq!(c.len(), 1); - assert!(c.contains(&String::from("armistice-"))); - - let c = w.get_completions("armistice-"); - assert_eq!(c.len(), 256); - - let c = w.get_completions("armistice-ba"); - assert_eq!( - c, - vec![ - "armistice-baboon", - "armistice-backfield", - "armistice-backward", - "armistice-banjo", - ] - ); - - let w = default_wordlist(3); - let c = w.get_completions("armistice-ba"); - assert_eq!( - c, - vec![ - "armistice-baboon-", - "armistice-backfield-", - "armistice-backward-", - "armistice-banjo-", - ] - ); - - let w = default_wordlist(4); - let c = w.get_completions("armistice-baboon"); - assert_eq!(c, vec!["armistice-baboon-"]); - } } diff --git a/src/lib.rs b/src/lib.rs index 3ad168e3..e7cbc158 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ #[macro_use] mod util; -mod core; +pub mod core; #[cfg(feature = "dilation")] pub mod dilated_transfer; #[cfg(feature = "dilation")] From 15a8be51d8885d2e5117d3ae9dd5c9cf8c872154 Mon Sep 17 00:00:00 2001 From: a-moreira Date: Fri, 7 Jul 2023 10:25:05 -0300 Subject: [PATCH 09/11] fmt --- cli/src/main.rs | 3 +-- src/core/test.rs | 15 +++++++++------ src/transfer/cancel.rs | 10 +++++++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 081a5d8c..55673d6c 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1180,8 +1180,7 @@ async fn receive_inner_v2( use dialoguer::Completion; use magic_wormhole::core::wordlist; -use std::collections::HashMap; -use std::fs; +use std::{collections::HashMap, fs}; struct WordList(PgpWordList); diff --git a/src/core/test.rs b/src/core/test.rs index 6123550b..2d852750 100644 --- a/src/core/test.rs +++ b/src/core/test.rs @@ -228,8 +228,9 @@ pub async fn test_file_rust2rust_deprecated() -> eyre::Result<()> { futures::future::pending(), ) .await? - .unwrap() - else {panic!("v2 should be disabled for now")}; + .unwrap() else { + panic!("v2 should be disabled for now") + }; req.accept( &transit::log_transit_connection, &mut answer, @@ -302,8 +303,9 @@ pub async fn test_file_rust2rust() -> eyre::Result<()> { futures::future::pending(), ) .await? - .unwrap() - else {panic!("v2 should be disabled for now")}; + .unwrap() else { + panic!("v2 should be disabled for now") + }; req.accept( &transit::log_transit_connection, &mut answer, @@ -413,8 +415,9 @@ pub async fn test_send_many() -> eyre::Result<()> { futures::future::pending(), ) .await? - .unwrap() - else {panic!("v2 should be disabled for now")}; + .unwrap() else { + panic!("v2 should be disabled for now") + }; // Hacky v1-compat conversion for now let mut answer = (gen_accept() diff --git a/src/transfer/cancel.rs b/src/transfer/cancel.rs index bb99ddbd..4837b0f9 100644 --- a/src/transfer/cancel.rs +++ b/src/transfer/cancel.rs @@ -49,7 +49,11 @@ macro_rules! with_cancel_wormhole { ($wormhole:ident, run = $run:expr, $cancel:expr, ret_cancel = $ret_cancel:expr $(,)?) => {{ let run = Box::pin($run); let result = cancel::cancellable_2(run, $cancel).await; - let Some((transit, wormhole, cancel)) = cancel::handle_run_result_noclose($wormhole, result).await? else { return Ok($ret_cancel); }; + let Some((transit, wormhole, cancel)) = + cancel::handle_run_result_noclose($wormhole, result).await? + else { + return Ok($ret_cancel); + }; (transit, wormhole, cancel) }}; } @@ -238,8 +242,8 @@ pub async fn handle_run_result_transit( */ loop { let Ok(msg) = transit.receive_record().await else { - break; - }; + break; + }; match parse_message(&msg) { Ok(None) => continue, Ok(Some(err)) => { From 987c9f4db9f79debb2d358b50a734c6ee18b7b64 Mon Sep 17 00:00:00 2001 From: a-moreira Date: Fri, 7 Jul 2023 10:28:30 -0300 Subject: [PATCH 10/11] remove unecessary deps from crate --- Cargo.lock | 1 - Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 368ea0f9..e5a80c42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1321,7 +1321,6 @@ dependencies = [ "base64", "bytecodec", "derive_more", - "dialoguer", "env_logger", "eyre", "futures", diff --git a/Cargo.toml b/Cargo.toml index 3b4af100..22ad3d68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ edition = "2021" rust-version = "1.66" # MSRV (also change in CI) [dependencies] -dialoguer = { version = "0.10", features = ["completion"] } serde = { version = "1.0.120", features = ["rc"] } serde_json = "1.0.61" serde_derive = "1.0.120" From ab352826537e38ba344c4a8f2bdd8399aa76d575 Mon Sep 17 00:00:00 2001 From: a-moreira Date: Thu, 13 Jul 2023 01:16:48 -0300 Subject: [PATCH 11/11] fix clippy --- cli/src/main.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 55673d6c..b58b97a0 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1238,8 +1238,6 @@ impl Completion for WordList { } if completions.len() == 1 { Some(completions.first().unwrap().clone()) - } else if completions.is_empty() { - None } else { // TODO: show vector of suggestions somehow // println!("Suggestions: {:#?}", &completions);