use crate::{
io::{Request, Response},
matchers::{MatchReport, Matcher, Matchers},
reports::{Report, ReportReason},
};
#[derive(Debug)]
struct Call {
request: Request,
reports: Option<Vec<MatchReport>>,
}
#[derive(Debug, Default)]
pub struct Expectation {
routing: Matchers,
validating: Matchers,
response: Response,
times: Option<u16>,
calls: Vec<Call>,
}
impl Expectation {
pub fn set_times(&mut self, times: u16) {
self.times = Some(times);
}
pub fn add_routing(&mut self, matcher: Matcher) {
self.routing.add(matcher);
}
pub fn add_validating(&mut self, matcher: Matcher) {
self.validating.add(matcher);
}
pub fn set_response_status(&mut self, status: u16) {
self.response.status = status;
}
pub fn set_response_header<K: Into<String>, V: Into<String>>(&mut self, key: K, value: V) {
self.response.headers.insert(key.into(), value.into());
}
pub fn set_response_body<S: Into<String>>(&mut self, body: S) {
self.response.body = Some(body.into());
}
pub fn matches(&self, request: &Request) -> bool {
self.routing.matches(request)
}
pub fn call(&mut self, request: Request) -> Response {
let reports = self.validating.mismatches(&request);
self.calls.push(Call { request, reports });
self.response.clone()
}
pub fn reports(&self) -> Option<Report> {
let request = self.routing.to_request();
if self.calls.is_empty() {
return Some(Report {
request,
reasons: vec![ReportReason::NoCall],
});
}
let mut reasons: Vec<ReportReason> = vec![];
if let Some(times) = self.times
&& times as usize != self.calls.len()
{
reasons.push(ReportReason::MismatchTimes {
expect: times,
actual: self.calls.len() as u16,
});
}
for call in self.calls.iter().filter_map(|req| {
req.reports.as_ref().map(|reports| ReportReason::Matcher {
request: Box::new(req.request.clone()),
reports: reports.clone(),
})
}) {
reasons.push(call);
}
if reasons.is_empty() {
None
} else {
Some(Report { request, reasons })
}
}
}