use crate::error::CowError;
pub fn normalize_private_key(private_key: &str) -> Result<String, CowError> {
if private_key.is_empty() {
return Err(CowError::Parse {
field: "private_key",
reason: "private key must be a non-empty string".to_owned(),
});
}
let clean = private_key.strip_prefix("0x").map_or(private_key, |v| v);
if clean.len() != 64 || !clean.bytes().all(|b| b.is_ascii_hexdigit()) {
return Err(CowError::Parse {
field: "private_key",
reason: "invalid private key format: must be exactly 64 hexadecimal characters (with or without 0x prefix)".to_owned(),
});
}
Ok(format!("0x{clean}"))
}
#[must_use]
pub fn is_valid_private_key(private_key: &str) -> bool {
normalize_private_key(private_key).is_ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn normalize_with_prefix() {
let key = format!("0x{}", "ab".repeat(32));
let result = normalize_private_key(&key).unwrap();
assert_eq!(result, key);
}
#[test]
fn normalize_without_prefix() {
let hex = "ab".repeat(32);
let result = normalize_private_key(&hex).unwrap();
assert_eq!(result, format!("0x{hex}"));
}
#[test]
fn normalize_empty_fails() {
assert!(normalize_private_key("").is_err());
}
#[test]
fn normalize_too_short_fails() {
assert!(normalize_private_key("0xabcd").is_err());
}
#[test]
fn normalize_invalid_hex_fails() {
let bad = format!("0x{}", "zz".repeat(32));
assert!(normalize_private_key(&bad).is_err());
}
#[test]
fn is_valid_true() {
let key = format!("0x{}", "12".repeat(32));
assert!(is_valid_private_key(&key));
}
#[test]
fn is_valid_false() {
assert!(!is_valid_private_key("invalid"));
}
}