use caramelo::MatchType::ToHave;
use caramelo::Matcher;
use caramelo::TypedMatcher;
use crate::matchers::HttpMatcher;
use crate::mock::Request;
pub use self::json::*;
pub use self::xml::*;
pub fn body(value: &str) -> HttpMatcher {
let regex = regex::Regex::new(value);
match regex {
Ok(regex) => HttpMatcher::Body(Body(regex)),
Err(_) => panic!("Invalid regex pattern"),
}
}
#[derive(Clone)]
pub struct Body(regex::Regex);
impl Matcher<Request> for Body {
fn matches(&self, value: &Request) -> bool {
if let Some(body) = &value.body() {
self.0
.is_match(&String::from_utf8_lossy(body))
} else {
false
}
}
fn description(&self) -> String {
format!("body contents matching {:?}", self.0)
}
}
impl TypedMatcher<Request> for Body {
fn matcher_type(&self) -> caramelo::MatchType {
ToHave
}
}
#[cfg(feature = "json")]
pub(crate) mod json {
use caramelo::{MatchType::ToHave, Matcher, TypedMatcher};
use jsonpath_rust::JsonPath;
use sonic_rs::Serialize;
use crate::{matchers::HttpMatcher, mock::Request};
pub fn exact_json_body<T: Serialize>(value: &T) -> HttpMatcher {
match sonic_rs::to_string(value) {
Ok(json) => HttpMatcher::ExactJson(BodyWithExactJson(json)),
Err(e) => panic!("Failed to serialize JSON: {}", e),
}
}
#[derive(Clone)]
pub struct BodyWithExactJson(String);
impl Matcher<Request> for BodyWithExactJson {
fn matches(&self, value: &Request) -> bool {
if let Some(body) = value.body() {
body == &self.0
} else {
false
}
}
fn description(&self) -> String {
format!("body contents matching {}", self.0)
}
}
impl TypedMatcher<Request> for BodyWithExactJson {
fn matcher_type(&self) -> caramelo::MatchType {
ToHave
}
}
pub fn partial_json_body(value: &str) -> HttpMatcher {
HttpMatcher::PartialJson(BodyWithPartialJson(value.to_owned()))
}
#[derive(Clone)]
pub struct BodyWithPartialJson(String);
impl Matcher<Request> for BodyWithPartialJson {
fn matches(&self, value: &Request) -> bool {
if let Some(body) = &value.body() {
if let Ok(json) =
sonic_rs::from_str::<serde_json::Value>(&String::from_utf8_lossy(body))
{
if let Ok(results) = json.query_with_path(self.0.as_str()) {
!results.is_empty()
} else {
false
}
} else {
false
}
} else {
false
}
}
fn description(&self) -> String {
format!("body contents containing {}", self.0)
}
}
impl TypedMatcher<Request> for BodyWithPartialJson {
fn matcher_type(&self) -> caramelo::MatchType {
ToHave
}
}
}
#[cfg(feature = "xml")]
pub(crate) mod xml {
use caramelo::{MatchType::ToHave, Matcher, TypedMatcher};
use serde::Serialize;
use serde_xml_rs::to_string;
use simdxml::parse;
use crate::{matchers::HttpMatcher, mock::Request};
pub fn exact_xml_body<T: Serialize>(value: &T) -> HttpMatcher {
match to_string(value) {
Ok(xml) => HttpMatcher::ExactXml(BodyWithExactXml(xml)),
Err(e) => panic!("Failed to serialize XML: {}", e),
}
}
#[derive(Clone)]
pub struct BodyWithExactXml(String);
impl Matcher<Request> for BodyWithExactXml {
fn matches(&self, value: &Request) -> bool {
if let Some(body) = value.body() {
body == &self.0
} else {
false
}
}
fn description(&self) -> String {
format!("body contents matching {}", self.0)
}
}
impl TypedMatcher<Request> for BodyWithExactXml {
fn matcher_type(&self) -> caramelo::MatchType {
ToHave
}
}
pub fn partial_xml_body(value: &str) -> HttpMatcher {
HttpMatcher::PartialXml(BodyWithPartialXml(value.to_owned()))
}
#[derive(Clone)]
pub struct BodyWithPartialXml(String);
impl Matcher<Request> for BodyWithPartialXml {
fn matches(&self, value: &Request) -> bool {
if let Some(body) = &value.body() {
if let Ok(xml) = parse(body) {
if let Ok(results) = xml.xpath_string(self.0.as_str()) {
!results.is_empty()
} else {
false
}
} else {
false
}
} else {
false
}
}
fn description(&self) -> String {
format!("body contents containing {}", self.0)
}
}
impl TypedMatcher<Request> for BodyWithPartialXml {
fn matcher_type(&self) -> caramelo::MatchType {
ToHave
}
}
}