num2phrase 0.1.0

Convert long number to a sequence of memorisable phrase with combination of short numbers
Documentation
use crate::wordlist::BIP39_LIST;
use strsim::levenshtein;
pub use crate::token_kind::{Score, Amount, TokenKind, TokenKindCorrect};


pub struct BIP39;

impl TokenKind for BIP39 {
    const MAX: usize = 2048;
    
    fn from_idx(&self, value: usize) -> Option<String> {
        if value < BIP39_LIST.len() {
            Some(BIP39_LIST[value].to_string())
        } else {
            None
        }
    }

    fn from_name(&self, name: &str) -> Option<usize> {
        BIP39_LIST.iter().position(|&w| w == name)
    }
}

impl TokenKindCorrect for BIP39 {
    fn suggest(&self, query: &str, amount: impl Amount) -> Vec<Score> {
        let mut suggestions: Vec<Score> = BIP39_LIST.iter()
            .map(|&w| Score { item: w.to_string(), score: levenshtein(query, w) })
            .collect();
        suggestions.sort_by_key(|s| s.score);
        match amount.amount() {
            Some(n) => suggestions.into_iter().take(n).collect(),
            None => suggestions
        }
    }
}

pub struct Digits;

impl TokenKind for Digits {
    const MAX: usize = 1000000;

    fn from_idx(&self, value: usize) -> Option<String> {
        if value < 1000000 {
            Some(value.to_string().clone())
        } else {
            None
        }
    }
    fn from_name(&self, name: &str) -> Option<usize> {
        match name.parse::<usize>() {
            Ok(num) if num < 1_000_000 => Some(num),
            _ => None,
        }
    }
}


// pub fn get_bip39(index: usize) -> Result<&'static str, String> {
//     if index < BIP39_LIST.len() {
//         Ok(BIP39_LIST[index])
//     } else {
//         Err(format!("Index {} out of bounds for BIP39 wordlist", index))
//     }
// }

// pub fn get_digit_from_bip39(word: &str) -> Result<usize, String> {
//     match BIP39_LIST.iter().position(|&w| w == word) {
//         Some(index) => Ok(index),
//         None => Err(format!("Word '{}' not found in BIP39 wordlist", word)),
//     }
// }

// pub fn suggest_bip39(word: &str, amount: impl Amount) -> Vec<(&'static str, usize)> {
//     let mut suggestions: Vec<(&'static str, usize)> = BIP39_LIST.iter()
//         .map(|&w| (w, levenshtein(word, w)))
//         .collect();
//     suggestions.sort_by_key(|&(_, dist)| dist);
//     match amount.amount() {
//         Some(n) => suggestions.into_iter().take(n).collect(),
//         None => suggestions
//     }
// }


#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_get_bip39() {
        let bip39_instance: BIP39 = BIP39 {};
        assert_eq!(bip39_instance.from_idx(0), Some("abandon".to_string()));
        assert_eq!(bip39_instance.from_idx(2047), Some("zoo".to_string()));
        assert!(bip39_instance.from_idx(2048).is_none());
    }

    #[test]
    fn both_way() {
        let bip39_instance: BIP39 = BIP39 {};
        for i in 0..BIP39_LIST.len() {
            let word = bip39_instance.from_idx(i).unwrap();
            let index = bip39_instance.from_name(&word).unwrap();
            assert_eq!(i, index);
        }
    }
}