use crate::error::FrameworkError;
pub const DEFAULT_COST: u32 = 12;
pub fn hash(password: &str) -> Result<String, FrameworkError> {
hash_with_cost(password, DEFAULT_COST)
}
pub fn hash_with_cost(password: &str, cost: u32) -> Result<String, FrameworkError> {
bcrypt::hash(password, cost)
.map_err(|e| FrameworkError::internal(format!("Password hash error: {e}")))
}
pub fn verify(password: &str, hash: &str) -> Result<bool, FrameworkError> {
bcrypt::verify(password, hash)
.map_err(|e| FrameworkError::internal(format!("Password verify error: {e}")))
}
pub fn needs_rehash(hash: &str) -> bool {
let parts: Vec<&str> = hash.split('$').collect();
if parts.len() < 4 {
return true; }
let cost: u32 = parts[2].parse().unwrap_or(0);
cost < DEFAULT_COST
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hash_and_verify() {
let password = "test_password_123";
let hashed = hash(password).expect("Hash should succeed");
assert!(hashed.starts_with("$2"));
assert!(verify(password, &hashed).expect("Verify should succeed"));
assert!(!verify("wrong_password", &hashed).expect("Verify should succeed"));
}
#[test]
fn test_hash_with_custom_cost() {
let password = "test";
let hashed = hash_with_cost(password, 4).expect("Hash should succeed");
assert!(verify(password, &hashed).expect("Verify should succeed"));
}
#[test]
fn test_needs_rehash() {
let low_cost_hash = hash_with_cost("test", 4).expect("Hash should succeed");
assert!(needs_rehash(&low_cost_hash));
let default_cost_hash = hash("test").expect("Hash should succeed");
assert!(!needs_rehash(&default_cost_hash));
}
}