Files
ppc_amnet/amnet-server/src/card.rs
T
2026-01-07 20:00:29 +00:00

95 lines
2.6 KiB
Rust

use std::{sync::{Mutex, OnceLock}, time::{Duration, Instant}};
use tracing::debug;
use crate::utils::{parse_access_code, parse_idm_hex};
#[derive(Debug, Clone)]
pub (crate) struct StoredCard {
pub access_code: [u8; 10],
pub card_idm: Option<[u8; 8]>,
pub expiry_time: Instant
}
impl StoredCard {
pub fn is_expired(&self) -> bool {
Instant::now() > self.expiry_time
}
}
pub (crate) enum CardPresentResult {
Accepted,
Rejected(String /* reason */),
SlotUnavailable(Instant /* release time */),
}
static PRESENTER: OnceLock<CardPresenter> = OnceLock::new();
#[derive(Debug)]
pub (crate) struct CardPresenter {
current_card: Mutex<Option<StoredCard>>,
}
impl Default for CardPresenter {
fn default() -> Self {
Self {
current_card: Mutex::new(None),
}
}
}
impl CardPresenter {
pub fn instance() -> &'static CardPresenter {
PRESENTER.get_or_init(|| CardPresenter::default())
}
pub fn request_card(&self, require_idm: Option<bool>) -> Option<StoredCard> {
let mut guard = self.current_card.lock().unwrap();
let card = guard.as_ref()?;
if let Some(require) = require_idm && card.card_idm.is_some() != require {
return None;
}
let card = guard.take();
if let Some(ref stored_card) = card && stored_card.is_expired() {
debug!("Stored card has expired and will not be provided");
return None;
};
card
}
pub fn present_card(&self, access_code: &String, idm_hex: Option<&String>, validity: Duration) -> CardPresentResult {
let mut guard = self.current_card.lock().unwrap();
if let Some(card) = guard.as_ref() && !card.is_expired() {
return CardPresentResult::SlotUnavailable(card.expiry_time);
}
let access_code_bytes = match parse_access_code(access_code) {
Some(bytes) => bytes,
None => return CardPresentResult::Rejected("Invalid access code format".to_string()),
};
let mut card_idm_bytes = None;
if let Some(idm_hex) = idm_hex {
match parse_idm_hex(idm_hex) {
Some(bytes) => card_idm_bytes = Some(bytes),
None => return CardPresentResult::Rejected("Invalid IDm format".to_string()),
}
}
let stored_card = StoredCard {
access_code: access_code_bytes,
card_idm: card_idm_bytes,
expiry_time: Instant::now() + validity,
};
*guard = Some(stored_card);
CardPresentResult::Accepted
}
}