Skip to main content

async_graphql_test/matchers/
mod.rs

1//! A library of assertions, provided as GraphQL directives.
2//!
3//! In most cases, you should just call [`register_directives`] to add those to your schema.
4
5use async_graphql::*;
6
7mod register_directives;
8pub use register_directives::register_directives;
9
10// We add a trailing underscore to the module names to avoid name conflicts with the re-exported funtions.
11#[path = "should_be_close_to.rs"]
12mod should_be_close_to_;
13#[path = "should_be_defined.rs"]
14mod should_be_defined_;
15#[path = "should_be_falsy.rs"]
16mod should_be_falsy_;
17#[path = "should_be_greater_than.rs"]
18mod should_be_greater_than_;
19#[path = "should_be_greater_than_or_equal.rs"]
20mod should_be_greater_than_or_equal_;
21#[path = "should_be_less_than.rs"]
22mod should_be_less_than_;
23#[path = "should_be_less_than_or_equal.rs"]
24mod should_be_less_than_or_equal_;
25#[path = "should_be_not_null.rs"]
26mod should_be_not_null_;
27#[path = "should_be_null.rs"]
28mod should_be_null_;
29#[path = "should_be_one_of.rs"]
30mod should_be_one_of_;
31#[path = "should_be_truthy.rs"]
32mod should_be_truthy_;
33#[path = "should_contain.rs"]
34mod should_contain_;
35#[path = "should_equal.rs"]
36mod should_equal_;
37#[path = "should_have_length.rs"]
38mod should_have_length_;
39#[path = "should_match.rs"]
40mod should_match_;
41#[path = "should_throw.rs"]
42mod should_throw_;
43
44pub use should_be_close_to_::should_be_close_to;
45pub use should_be_defined_::should_be_defined;
46pub use should_be_falsy_::should_be_falsy;
47pub use should_be_greater_than_::should_be_greater_than;
48pub use should_be_greater_than_or_equal_::should_be_greater_than_or_equal;
49pub use should_be_less_than_::should_be_less_than;
50pub use should_be_less_than_or_equal_::should_be_less_than_or_equal;
51pub use should_be_not_null_::should_be_not_null;
52pub use should_be_null_::should_be_null;
53pub use should_be_one_of_::should_be_one_of;
54pub use should_be_truthy_::should_be_truthy;
55pub use should_contain_::should_contain;
56pub use should_equal_::should_equal;
57pub use should_have_length_::should_have_length;
58pub use should_match_::should_match;
59pub use should_throw_::should_throw;
60
61#[derive(Debug)]
62pub struct MatcherError;
63
64impl MatcherError {
65    #[expect(clippy::new_ret_no_self)]
66    pub fn new(pos: Pos, message: String) -> ServerError {
67        ServerError {
68            source: Some(std::sync::Arc::new(MatcherError)),
69            ..ServerError::new(message, Some(pos))
70        }
71    }
72
73    pub fn unexpected_error(mut err: ServerError) -> ServerError {
74        err.message = format!("Unexpected error occurred:\n\n{}", err.message);
75        err.source = Some(std::sync::Arc::new(MatcherError));
76        err
77    }
78}
79
80struct ValueTypeName<'a>(&'a Value);
81
82impl std::fmt::Display for ValueTypeName<'_> {
83    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84        f.write_str(match self.0 {
85            Value::Null => "null",
86            Value::Number(_) => "number",
87            Value::String(_) => "string",
88            Value::Boolean(_) => "boolean",
89            Value::Binary(_) => "binary",
90            Value::Enum(_) => "enum",
91            Value::List(_) => "list", // probably should be [Type!]!
92            Value::Object(_) => "object",
93        })
94    }
95}
96
97enum Numeric {
98    Int(i64),
99    Float(f64),
100    /// Long arithmetics
101    String(String),
102}
103
104impl std::fmt::Display for Numeric {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        match self {
107            Numeric::Int(x) => x.fmt(f),
108            Numeric::Float(x) => x.fmt(f),
109            Numeric::String(x) => x.fmt(f),
110        }
111    }
112}
113
114impl PartialEq<serde_json::Number> for Numeric {
115    fn eq(&self, other: &serde_json::Number) -> bool {
116        match self {
117            &Numeric::Int(x) => {
118                (other.is_i64() && other.as_i64() == Some(x))
119                    || (other.is_u64() && other.as_u64() == Some(x as u64))
120                    || (other.is_f64() && other.as_f64() == Some(x as f64))
121            }
122            &Numeric::Float(x) => other.as_f64() == Some(x),
123            Numeric::String(_) => unimplemented!(),
124        }
125    }
126}
127
128impl PartialOrd<serde_json::Number> for Numeric {
129    fn partial_cmp(&self, other: &serde_json::Number) -> Option<std::cmp::Ordering> {
130        match self {
131            &Numeric::Int(x) => match other {
132                other if other.is_i64() => other.as_i64().partial_cmp(&Some(x)),
133                other if other.is_u64() => other.as_u64().partial_cmp(&Some(x as u64)),
134                other if other.is_f64() => other.as_f64().partial_cmp(&Some(x as f64)),
135                _ => unreachable!(
136                    "serde_json::Number shouldn't have any other representations, see its definition"
137                ),
138            },
139            &Numeric::Float(x) => other.as_f64().partial_cmp(&Some(x)),
140            Numeric::String(_) => unimplemented!(),
141        }
142    }
143}