use std::fmt::{Display, Formatter};
use async_graphql::async_trait::async_trait;
use async_graphql::{Any, Context, CustomDirective, Directive, ResolveFut, ServerResult, Value};
use super::{MatcherError, ValueTypeName};
#[Directive(location = "Field", name = "shouldContain")]
pub fn should_contain(item: Any) -> impl CustomDirective {
ShouldContain { item: item.0 }
}
struct ShouldContain {
item: Value,
}
impl Display for ShouldContain {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.item.fmt(f)
}
}
#[async_trait]
impl CustomDirective for ShouldContain {
async fn resolve_field(
&self,
ctx: &Context<'_>,
resolve: ResolveFut<'_>,
) -> ServerResult<Option<Value>> {
match resolve.await {
Ok(None) => Ok(None),
Ok(Some(Value::List(list))) => {
if list.iter().any(|it| self.item.eq(it)) {
Ok(Some(Value::List(list)))
} else {
Err(MatcherError::new(
ctx.item.pos,
format!(
"Expected value: {}\nReceived array: {}",
self.item,
Value::List(list)
),
))
}
}
Ok(Some(Value::String(string))) => {
if let Value::String(value) = &self.item {
if string.contains(value) {
Ok(Some(Value::String(string)))
} else {
Err(MatcherError::new(
ctx.item.pos,
format!(
"Expected value to match:\n {self}\nReceived:\n \"{string}\""
),
))
}
} else {
let type_name = ValueTypeName(&self.item);
Err(MatcherError::new(
ctx.item.pos,
format!(
"@shouldContain error: passed value must be a string.\nPassed has type: {type_name}\nReceived has value: {self}"
),
))
}
}
Ok(Some(value)) => {
let type_name = ValueTypeName(&value);
Err(MatcherError::new(
ctx.item.pos,
format!(
"@shouldContain error: received value must be an array or a string.\nReceived has type: {type_name}\nReceived has value: {value}"
),
))
}
Err(err) => Err(MatcherError::unexpected_error(err)),
}
}
}