use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::OnceLock;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum StatusError {
#[error("Status code or message does not exist")]
NotFound,
#[error("Failed to read status codes file: {0}")]
FileError(#[from] std::io::Error),
#[error("Failed to parse JSON data: {0}")]
JsonError(#[from] serde_json::Error),
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Status {
pub code: String,
pub message: String,
}
static CODE_TO_MESSAGE: OnceLock<HashMap<String, String>> = OnceLock::new();
static MESSAGE_TO_CODE: OnceLock<HashMap<String, String>> = OnceLock::new();
fn normalize_key(s: &str) -> String {
s.trim().to_lowercase()
}
fn load_status_maps() -> Result<(HashMap<String, String>, HashMap<String, String>), StatusError> {
let json_content = std::fs::read_to_string("codes.json")?;
let statuses: Vec<Status> = serde_json::from_str(&json_content)?;
let mut code_to_message = HashMap::with_capacity(statuses.len());
let mut message_to_code = HashMap::with_capacity(statuses.len());
for status in statuses {
let normalized_code = normalize_key(&status.code);
let normalized_message = normalize_key(&status.message);
code_to_message.insert(normalized_code, status.message.clone());
message_to_code.insert(normalized_message, status.code.clone());
}
#[cfg(debug_assertions)]
{
println!("Loaded {} status codes", code_to_message.len());
println!("Code-to-message cache: {:?}", code_to_message);
println!("Message-to-code cache: {:?}", message_to_code);
}
Ok((code_to_message, message_to_code))
}
fn get_code_to_message() -> &'static HashMap<String, String> {
CODE_TO_MESSAGE.get_or_init(|| {
let (code_to_message, _) =
load_status_maps().expect("Failed to load status codes from file");
code_to_message
})
}
fn get_message_to_code() -> &'static HashMap<String, String> {
MESSAGE_TO_CODE.get_or_init(|| {
let (_, message_to_code) =
load_status_maps().expect("Failed to load status codes from file");
message_to_code
})
}
pub fn code(message: &str) -> Result<String, StatusError> {
get_message_to_code()
.get(&normalize_key(message))
.cloned()
.ok_or(StatusError::NotFound)
}
pub fn message(code: &str) -> Result<String, StatusError> {
get_code_to_message()
.get(&normalize_key(code))
.cloned()
.ok_or(StatusError::NotFound)
}
pub fn is_valid_code(code: &str) -> bool {
get_code_to_message().contains_key(&normalize_key(code))
}
pub fn is_valid_message(message: &str) -> bool {
get_message_to_code().contains_key(&normalize_key(message))
}
pub fn all_codes() -> Vec<String> {
get_message_to_code().values().cloned().collect()
}
pub fn all_messages() -> Vec<String> {
get_code_to_message().values().cloned().collect()
}