Skip to main content

async_graphql_test/matchers/
should_have_length.rs

1use async_graphql::async_trait::async_trait;
2use async_graphql::{Context, CustomDirective, Directive, ResolveFut, ServerResult, Value};
3
4use super::{MatcherError, ValueTypeName};
5
6/// Use `@shouldHaveLength` to check that an object has a `.length` property, and it is set to a certain numeric value.
7///
8/// This is especially useful for checking arrays or strings size.
9#[Directive(location = "Field", name = "shouldHaveLength")]
10pub fn should_have_length(n: usize) -> impl CustomDirective {
11    ShouldHaveLength { len: n }
12}
13
14struct ShouldHaveLength {
15    len: usize,
16}
17
18#[async_trait]
19impl CustomDirective for ShouldHaveLength {
20    async fn resolve_field(
21        &self,
22        ctx: &Context<'_>,
23        resolve: ResolveFut<'_>,
24    ) -> ServerResult<Option<Value>> {
25        match resolve.await {
26            Ok(None) => Ok(None),
27            // todo: returned HashMap can also have length
28            Ok(Some(Value::List(list))) => {
29                if list.len() == self.len {
30                    Ok(Some(Value::List(list)))
31                } else {
32                    Err(MatcherError::new(
33                        ctx.item.pos,
34                        format!(
35                            "Expected length: {}\nReceived length: {}\nReceived array:  {}",
36                            self.len,
37                            list.len(),
38                            Value::List(list)
39                        ),
40                    ))
41                }
42            }
43            Ok(Some(Value::String(string))) => {
44                if string.len() == self.len {
45                    Ok(Some(Value::String(string)))
46                } else {
47                    Err(MatcherError::new(
48                        ctx.item.pos,
49                        format!(
50                            "Expected length: {}\nReceived length: {}\nReceived string: {}",
51                            self.len,
52                            string.len(),
53                            Value::String(string)
54                        ),
55                    ))
56                }
57            }
58            Ok(Some(value)) => {
59                let type_name = ValueTypeName(&value);
60                Err(MatcherError::new(
61                    ctx.item.pos,
62                    format!(
63                        "@shouldHaveLength error: value must have a length property.\nReceived has type:  {type_name}\nReceived has value: {value}"
64                    ),
65                ))
66            }
67            Err(err) => Err(MatcherError::unexpected_error(err)),
68        }
69    }
70}