diff --git a/cli/src/completer.rs b/cli/src/completer.rs index 8ed0b0b0..577d444b 100644 --- a/cli/src/completer.rs +++ b/cli/src/completer.rs @@ -16,6 +16,7 @@ impl CustomCompletion { impl Completion for CustomCompletion { fn get(&self, input: &str) -> Option { + dbg!(WORDLIST.get_completions(input)); WORDLIST.get_completions(input).first().cloned() } } diff --git a/src/core.rs b/src/core.rs index 3696c495..04b1828b 100644 --- a/src/core.rs +++ b/src/core.rs @@ -5,7 +5,6 @@ pub mod rendezvous; mod server_messages; #[cfg(test)] mod test; -#[doc(hidden)] pub mod wordlist; use serde_derive::{Deserialize, Serialize}; diff --git a/src/core/wordlist.rs b/src/core/wordlist.rs index 7ad2280d..7f9c125f 100644 --- a/src/core/wordlist.rs +++ b/src/core/wordlist.rs @@ -2,9 +2,13 @@ use rand::{rngs::OsRng, seq::SliceRandom}; use serde_json::{self, Value}; use std::fmt; +/// Represents a list of words used to generate and complete wormhole codes. +/// A wormhole code is a sequence of words used for secure communication or identification. #[derive(PartialEq)] pub struct Wordlist { + /// Number of words in a wormhole code num_words: usize, + /// Odd and even wordlist words: Vec>, } @@ -20,6 +24,10 @@ impl Wordlist { Wordlist { num_words, words } } + /// Completes a wormhole code + /// + /// Completion can be done either with fuzzy search (approximate string matching) + /// or simple `starts_with` matching. pub fn get_completions(&self, prefix: &str) -> Vec { let count_dashes = prefix.matches('-').count(); let words = &self.words[count_dashes % self.words.len()]; @@ -51,17 +59,16 @@ impl Wordlist { } /// Get either even or odd wordlist - pub fn get_wordlist(&self, prefix: &str, cursor_pos: Option) -> &Vec { - let limited_prefix = match cursor_pos { - Some(pos) if pos < prefix.len() => &prefix[..pos], - _ => prefix, - }; - let count_dashes = limited_prefix.matches('-').count(); + pub fn get_wordlist(&self, prefix: &str) -> &Vec { + let count_dashes = prefix.matches('-').count(); &self.words[count_dashes % self.words.len()] } + /// Fuzzy wormhole code completion #[cfg(feature = "fuzzy-complete")] fn fuzzy_complete(&self, partial: &str, words: &[String]) -> Vec { + // We use Jaro-Winkler algorithm because it emphasizes the beginning of a word + use fuzzt::algorithms::JaroWinkler; let words = words.iter().map(|w| w.as_str()).collect::>(); @@ -72,6 +79,7 @@ impl Wordlist { .collect() } + /// Choose wormhole code word pub fn choose_words(&self) -> String { let mut rng = OsRng; let components: Vec = self @@ -85,17 +93,6 @@ impl Wordlist { } } -/// Extract partial str from prefix with cursor position -pub fn extract_partial_from_prefix<'a>(prefix: &'a str, pos: usize) -> &'a str { - let current_word_start = prefix[..pos].rfind('-').map(|i| i + 1).unwrap_or(0); - let current_word_end = prefix[pos..] - .find('-') - .map(|i| i + pos) - .unwrap_or_else(|| prefix.len()); - - &prefix[current_word_start..current_word_end] -} - fn load_pgpwords() -> Vec> { let raw_words_value: Value = serde_json::from_str(include_str!("pgpwords.json")).unwrap(); let raw_words = raw_words_value.as_object().unwrap(); @@ -124,6 +121,7 @@ fn load_pgpwords() -> Vec> { vec![even_words, odd_words] } +/// Construct Wordlist struct with given number of words in a wormhole code pub fn default_wordlist(num_words: usize) -> Wordlist { Wordlist { num_words, @@ -211,10 +209,21 @@ mod test { let list = default_wordlist(2); assert_eq!(list.get_completions("22"), Vec::::new()); + assert_eq!(list.get_completions("22-"), Vec::::new()); + + // Invalid wormhole code check + assert_eq!(list.get_completions("trj"), Vec::::new()); assert_eq!( list.get_completions("22-chisel"), ["22-chisel", "22-chairlift", "22-christmas"] ); + + assert_eq!( + list.get_completions("22-chle"), + ["22-chisel", "22-chatter", "22-checkup"] + ); + + assert_eq!(list.get_completions("22-chisel-tba"), ["22-chisel-tobacco"]); } }