use crate::action::Action;
use crate::matcher::{Matcher, SoapRequest};
use crate::responder::{ResponseBody, Responder};
use std::net::SocketAddr;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::RwLock;
#[derive(Debug, Clone)]
pub struct ReceivedRequest {
pub action_name: String,
pub service_type: String,
pub body: crate::matcher::SoapRequestBody,
pub timestamp: std::time::Duration,
}
impl ReceivedRequest {
pub(crate) fn from_soap_request(request: &SoapRequest, start_time: Instant) -> Self {
ReceivedRequest {
action_name: request.action_name.clone(),
service_type: request.service_type.clone(),
body: request.body.clone(),
timestamp: start_time.elapsed(),
}
}
}
#[derive(Debug, Clone)]
pub struct ReceivedSsdpRequest {
pub source: SocketAddr,
pub search_target: String,
pub man: String,
pub mx: Option<u32>,
pub raw: String,
pub timestamp: std::time::Duration,
}
pub(crate) struct Mock {
action: Action,
responder: Responder,
priority: u32,
max_times: Option<u32>,
match_count: AtomicU32,
}
impl Mock {
pub fn new(action: impl Into<Action>, responder: impl Into<Responder>) -> Self {
Mock {
action: action.into(),
responder: responder.into(),
priority: 0,
max_times: None,
match_count: AtomicU32::new(0),
}
}
pub fn with_priority(mut self, priority: u32) -> Self {
self.priority = priority;
self
}
pub fn times(mut self, n: u32) -> Self {
self.max_times = Some(n);
self
}
pub fn matches(&self, request: &SoapRequest) -> bool {
if let Some(max) = self.max_times {
if self.match_count.load(Ordering::SeqCst) >= max {
return false;
}
}
self.action.matches(request)
}
pub fn respond(&self, request: &SoapRequest) -> ResponseBody {
self.match_count.fetch_add(1, Ordering::SeqCst);
self.responder.respond(request)
}
pub fn priority(&self) -> u32 {
self.priority
}
}
impl std::fmt::Debug for Mock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Mock")
.field("action", &self.action)
.field("responder", &self.responder)
.field("priority", &self.priority)
.field("max_times", &self.max_times)
.field("match_count", &self.match_count.load(Ordering::SeqCst))
.finish()
}
}
pub(crate) struct MockRegistry {
mocks: RwLock<Vec<Arc<Mock>>>,
received_requests: RwLock<Vec<ReceivedRequest>>,
received_ssdp_requests: RwLock<Vec<ReceivedSsdpRequest>>,
start_time: Instant,
}
impl MockRegistry {
pub fn new() -> Self {
MockRegistry {
mocks: RwLock::new(Vec::new()),
received_requests: RwLock::new(Vec::new()),
received_ssdp_requests: RwLock::new(Vec::new()),
start_time: Instant::now(),
}
}
pub async fn register(&self, mock: Mock) {
let mut mocks = self.mocks.write().await;
mocks.push(Arc::new(mock));
mocks.sort_by(|a, b| b.priority().cmp(&a.priority()));
}
pub async fn find_response(&self, request: &SoapRequest) -> Option<ResponseBody> {
{
let received = ReceivedRequest::from_soap_request(request, self.start_time);
let mut requests = self.received_requests.write().await;
requests.push(received);
}
let mocks = self.mocks.read().await;
for mock in mocks.iter() {
if mock.matches(request) {
return Some(mock.respond(request));
}
}
None
}
pub async fn received_requests(&self) -> Vec<ReceivedRequest> {
let requests = self.received_requests.read().await;
requests.clone()
}
pub async fn clear(&self) {
let mut mocks = self.mocks.write().await;
mocks.clear();
}
pub async fn clear_received_requests(&self) {
let mut requests = self.received_requests.write().await;
requests.clear();
}
pub async fn record_ssdp_request(&self, request: ReceivedSsdpRequest) {
let mut requests = self.received_ssdp_requests.write().await;
requests.push(request);
}
pub async fn received_ssdp_requests(&self) -> Vec<ReceivedSsdpRequest> {
let requests = self.received_ssdp_requests.read().await;
requests.clone()
}
pub async fn clear_received_ssdp_requests(&self) {
let mut requests = self.received_ssdp_requests.write().await;
requests.clear();
}
pub fn start_time(&self) -> Instant {
self.start_time
}
}