use crate::error::SxurlError;
use crate::types::UrlComponents;
use crate::core::hasher::ComponentHasher;
pub fn pack_sxurl(components: &UrlComponents) -> Result<[u8; 32], SxurlError> {
let tld_hash = ComponentHasher::hash_tld(&components.tld)?;
let domain_hash = ComponentHasher::hash_domain(&components.domain)?;
let subdomain_hash = if components.subdomain.is_empty() {
0
} else {
ComponentHasher::hash_subdomain(&components.subdomain)?
};
let path_hash = ComponentHasher::hash_path(&components.path)?;
let params_hash = if components.query.is_empty() {
0
} else {
ComponentHasher::hash_params(&components.query)?
};
let fragment_hash = if components.fragment.is_empty() {
0
} else {
ComponentHasher::hash_fragment(&components.fragment)?
};
let scheme_code = match components.scheme.as_str() {
"https" => 0u64,
"http" => 1u64,
"ftp" => 2u64,
_ => return Err(SxurlError::InvalidScheme),
};
let flags =
(if !components.subdomain.is_empty() { 1u64 } else { 0u64 }) << 4 |
(if !components.query.is_empty() { 1u64 } else { 0u64 }) << 3 |
(if !components.fragment.is_empty() { 1u64 } else { 0u64 }) << 2 |
(if components.port != get_default_port(&components.scheme) { 1u64 } else { 0u64 }) << 1;
let header_value = (1u64 << 8) | (scheme_code << 5) | flags;
let mut hex_string = String::with_capacity(64);
hex_string.push_str(&format!("{:03x}", header_value));
hex_string.push_str(&format!("{:04x}", tld_hash));
hex_string.push_str(&format!("{:015x}", domain_hash));
hex_string.push_str(&format!("{:08x}", subdomain_hash));
hex_string.push_str(&format!("{:04x}", components.port));
hex_string.push_str(&format!("{:015x}", path_hash));
hex_string.push_str(&format!("{:09x}", params_hash));
hex_string.push_str(&format!("{:06x}", fragment_hash));
if hex_string.len() != 64 {
return Err(SxurlError::InternalError);
}
hex_to_sxurl(&hex_string)
}
fn get_default_port(scheme: &str) -> u16 {
match scheme {
"http" => 80,
"https" => 443,
"ftp" => 21,
_ => 0,
}
}
pub fn sxurl_to_hex(sxurl: &[u8; 32]) -> String {
hex::encode(sxurl)
}
pub fn hex_to_sxurl(hex_str: &str) -> Result<[u8; 32], SxurlError> {
if hex_str.len() != 64 {
return Err(SxurlError::InvalidLength);
}
let bytes = hex::decode(hex_str)
.map_err(|_| SxurlError::InvalidHexCharacter)?;
if bytes.len() != 32 {
return Err(SxurlError::InvalidLength);
}
let mut result = [0u8; 32];
result.copy_from_slice(&bytes);
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sxurl_hex_conversion() {
let sxurl = [0u8; 32];
let hex = sxurl_to_hex(&sxurl);
assert_eq!(hex.len(), 64);
assert_eq!(hex, "0".repeat(64));
let parsed = hex_to_sxurl(&hex).unwrap();
assert_eq!(parsed, sxurl);
}
#[test]
fn test_pack_sxurl_basic() {
let components = UrlComponents::new(
"https".to_string(),
"com".to_string(),
"example".to_string(),
"".to_string(),
443,
"/".to_string(),
"".to_string(),
"".to_string(),
);
let result = pack_sxurl(&components);
assert!(result.is_ok());
let sxurl = result.unwrap();
assert_eq!(sxurl.len(), 32);
let hex = sxurl_to_hex(&sxurl);
assert_eq!(hex.len(), 64);
assert!(hex.starts_with("100"), "HTTPS with no flags should start with '100', got: {}", &hex[0..3]);
println!("SXURL for https://example.com/: {}", hex);
}
#[test]
fn test_pack_sxurl_with_params() {
let components = UrlComponents::new(
"https".to_string(),
"com".to_string(),
"google".to_string(),
"".to_string(),
443,
"/search".to_string(),
"q=test".to_string(),
"".to_string(),
);
let result = pack_sxurl(&components);
assert!(result.is_ok());
let sxurl = result.unwrap();
let hex = sxurl_to_hex(&sxurl);
assert!(hex.starts_with("108"), "HTTPS with params should start with '108', got: {}", &hex[0..3]);
println!("SXURL for https://google.com/search?q=test: {}", hex);
}
}