use crate::error::{ImportError, Result};
use crate::types::{ImportFormat, ImportResult};
use crate::{import_wif, import_hex, import_mini_key, import_mnemonic, MnemonicImport};
use rustywallet_hd::Network as HdNetwork;
use rustywallet_keys::prelude::Network as KeysNetwork;
fn convert_network(network: KeysNetwork) -> HdNetwork {
match network {
KeysNetwork::Mainnet => HdNetwork::Mainnet,
KeysNetwork::Testnet => HdNetwork::Testnet,
}
}
pub fn detect_format(input: &str) -> Option<ImportFormat> {
let input = input.trim();
if input.starts_with("6P") && input.len() == 58 {
return Some(ImportFormat::Bip38);
}
if (input.len() == 51 || input.len() == 52) &&
(input.starts_with('5') || input.starts_with('K') ||
input.starts_with('L') || input.starts_with('9') ||
input.starts_with('c')) {
return Some(ImportFormat::Wif);
}
let hex_input = input.strip_prefix("0x").or_else(|| input.strip_prefix("0X")).unwrap_or(input);
if hex_input.len() == 64 && hex_input.chars().all(|c| c.is_ascii_hexdigit()) {
return Some(ImportFormat::Hex);
}
if input.starts_with('S') && (input.len() == 22 || input.len() == 30) {
return Some(ImportFormat::MiniKey);
}
let words: Vec<&str> = input.split_whitespace().collect();
if [12, 15, 18, 21, 24].contains(&words.len()) {
return Some(ImportFormat::Mnemonic);
}
None
}
pub fn import_any(input: &str) -> Result<ImportResult> {
let input = input.trim();
let format = detect_format(input)
.ok_or_else(|| ImportError::InvalidFormat("Could not detect format".to_string()))?;
match format {
ImportFormat::Wif => {
let (key, network, compressed) = import_wif(input)?;
Ok(ImportResult::new(key, ImportFormat::Wif)
.with_network(convert_network(network))
.with_compressed(compressed))
}
ImportFormat::Hex => {
let key = import_hex(input)?;
Ok(ImportResult::new(key, ImportFormat::Hex)
.with_compressed(true))
}
ImportFormat::MiniKey => {
let key = import_mini_key(input)?;
Ok(ImportResult::new(key, ImportFormat::MiniKey)
.with_compressed(false)) }
ImportFormat::Mnemonic => {
let config = MnemonicImport::new(input);
import_mnemonic(config)
}
ImportFormat::Bip38 => {
Err(ImportError::InvalidFormat(
"BIP38 requires password - use import_bip38() directly".to_string()
))
}
ImportFormat::ElectrumSeed => {
Err(ImportError::UnsupportedFormat(
"Electrum seed format not yet supported".to_string()
))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_wif_uncompressed() {
let wif = "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ";
assert_eq!(detect_format(wif), Some(ImportFormat::Wif));
}
#[test]
fn test_detect_wif_compressed() {
let wif = "KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617";
assert_eq!(detect_format(wif), Some(ImportFormat::Wif));
}
#[test]
fn test_detect_hex() {
let hex = "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d";
assert_eq!(detect_format(hex), Some(ImportFormat::Hex));
}
#[test]
fn test_detect_hex_with_prefix() {
let hex = "0x0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d";
assert_eq!(detect_format(hex), Some(ImportFormat::Hex));
}
#[test]
fn test_detect_bip38() {
let bip38 = "6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg";
assert_eq!(detect_format(bip38), Some(ImportFormat::Bip38));
}
#[test]
fn test_detect_mini_key() {
let mini = "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy";
assert_eq!(detect_format(mini), Some(ImportFormat::MiniKey));
}
#[test]
fn test_detect_mnemonic() {
let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
assert_eq!(detect_format(mnemonic), Some(ImportFormat::Mnemonic));
}
#[test]
fn test_detect_unknown() {
let unknown = "not a valid format";
assert_eq!(detect_format(unknown), None);
}
#[test]
fn test_import_any_wif() {
let wif = "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ";
let result = import_any(wif).unwrap();
assert_eq!(result.format, ImportFormat::Wif);
assert!(!result.compressed);
}
#[test]
fn test_import_any_hex() {
let hex = "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d";
let result = import_any(hex).unwrap();
assert_eq!(result.format, ImportFormat::Hex);
}
#[test]
fn test_import_any_mnemonic() {
let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
let result = import_any(mnemonic).unwrap();
assert_eq!(result.format, ImportFormat::Mnemonic);
}
#[test]
fn test_import_any_bip38_requires_password() {
let bip38 = "6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg";
let result = import_any(bip38);
assert!(matches!(result, Err(ImportError::InvalidFormat(_))));
}
}