1use std::collections::HashMap;
6use std::fmt;
7
8pub type ResolverResult = Result<serde_json::Value, ResolverError>;
10
11#[derive(Debug)]
13pub enum ResolverError {
14 FieldNotFound(String),
16 MiddlewareError(String),
18 HandlerError(String),
20}
21
22impl fmt::Display for ResolverError {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 ResolverError::FieldNotFound(name) => write!(f, "Field not found: {name}"),
26 ResolverError::MiddlewareError(msg) => write!(f, "Middleware error: {msg}"),
27 ResolverError::HandlerError(msg) => write!(f, "Handler error: {msg}"),
28 }
29 }
30}
31
32impl std::error::Error for ResolverError {}
33
34#[derive(Debug, Clone)]
36pub struct ResolverContext {
37 pub field_name: String,
39 pub parent_value: Option<serde_json::Value>,
41 pub args: HashMap<String, serde_json::Value>,
43}
44
45impl ResolverContext {
46 pub fn new(field_name: impl Into<String>) -> Self {
48 Self {
49 field_name: field_name.into(),
50 parent_value: None,
51 args: HashMap::new(),
52 }
53 }
54
55 pub fn with_parent(mut self, parent: serde_json::Value) -> Self {
57 self.parent_value = Some(parent);
58 self
59 }
60
61 pub fn with_arg(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
63 self.args.insert(key.into(), value);
64 self
65 }
66}
67
68pub trait FieldMiddleware: Send + Sync {
72 fn name(&self) -> &str;
74
75 fn process(
77 &self,
78 ctx: &ResolverContext,
79 next: &dyn Fn(&ResolverContext) -> ResolverResult,
80 ) -> ResolverResult;
81}
82
83type HandlerFn = Box<dyn Fn(&ResolverContext) -> ResolverResult + Send + Sync>;
85
86pub struct FieldResolver {
90 middlewares: Vec<Box<dyn FieldMiddleware>>,
91 handlers: HashMap<String, HandlerFn>,
92}
93
94impl FieldResolver {
95 pub fn new() -> Self {
97 Self {
98 middlewares: Vec::new(),
99 handlers: HashMap::new(),
100 }
101 }
102
103 pub fn add_middleware(&mut self, mw: Box<dyn FieldMiddleware>) {
107 self.middlewares.push(mw);
108 }
109
110 pub fn register(
112 &mut self,
113 field: impl Into<String>,
114 handler: impl Fn(&ResolverContext) -> ResolverResult + Send + Sync + 'static,
115 ) {
116 self.handlers.insert(field.into(), Box::new(handler));
117 }
118
119 pub fn resolve(&self, ctx: ResolverContext) -> ResolverResult {
121 let field_name = ctx.field_name.clone();
123 let handler = self
124 .handlers
125 .get(&field_name)
126 .ok_or_else(|| ResolverError::FieldNotFound(field_name.clone()))?;
127
128 let base: &dyn Fn(&ResolverContext) -> ResolverResult = handler.as_ref();
130
131 self.run_middleware(&ctx, 0, base)
135 }
136
137 fn run_middleware(
138 &self,
139 ctx: &ResolverContext,
140 idx: usize,
141 final_handler: &dyn Fn(&ResolverContext) -> ResolverResult,
142 ) -> ResolverResult {
143 if idx >= self.middlewares.len() {
144 return final_handler(ctx);
145 }
146
147 let mw = &self.middlewares[idx];
148 let next_idx = idx + 1;
149
150 let next = |next_ctx: &ResolverContext| -> ResolverResult {
152 self.run_middleware(next_ctx, next_idx, final_handler)
153 };
154
155 mw.process(ctx, &next)
156 }
157
158 pub fn middleware_count(&self) -> usize {
160 self.middlewares.len()
161 }
162
163 pub fn registered_fields(&self) -> Vec<&str> {
165 let mut fields: Vec<&str> = self.handlers.keys().map(String::as_str).collect();
166 fields.sort_unstable();
167 fields
168 }
169
170 pub fn has_field(&self, field: &str) -> bool {
172 self.handlers.contains_key(field)
173 }
174}
175
176impl Default for FieldResolver {
177 fn default() -> Self {
178 Self::new()
179 }
180}
181
182pub struct LoggingMiddleware {
186 pub prefix: String,
187}
188
189impl LoggingMiddleware {
190 pub fn new(prefix: impl Into<String>) -> Self {
191 Self {
192 prefix: prefix.into(),
193 }
194 }
195}
196
197impl FieldMiddleware for LoggingMiddleware {
198 fn name(&self) -> &str {
199 "logging"
200 }
201
202 fn process(
203 &self,
204 ctx: &ResolverContext,
205 next: &dyn Fn(&ResolverContext) -> ResolverResult,
206 ) -> ResolverResult {
207 let _log = format!("{} resolving field: {}", self.prefix, ctx.field_name);
208 next(ctx)
209 }
210}
211
212pub struct ErrorMiddleware {
214 message: String,
215}
216
217impl ErrorMiddleware {
218 pub fn new(message: impl Into<String>) -> Self {
219 Self {
220 message: message.into(),
221 }
222 }
223}
224
225impl FieldMiddleware for ErrorMiddleware {
226 fn name(&self) -> &str {
227 "error_injector"
228 }
229
230 fn process(
231 &self,
232 _ctx: &ResolverContext,
233 _next: &dyn Fn(&ResolverContext) -> ResolverResult,
234 ) -> ResolverResult {
235 Err(ResolverError::MiddlewareError(self.message.clone()))
236 }
237}
238
239pub struct UppercaseFieldMiddleware;
241
242impl FieldMiddleware for UppercaseFieldMiddleware {
243 fn name(&self) -> &str {
244 "uppercase_field"
245 }
246
247 fn process(
248 &self,
249 ctx: &ResolverContext,
250 next: &dyn Fn(&ResolverContext) -> ResolverResult,
251 ) -> ResolverResult {
252 let mut new_ctx = ctx.clone();
253 new_ctx.field_name = ctx.field_name.to_uppercase();
254 next(&new_ctx)
255 }
256}
257
258pub struct PassthroughMiddleware;
260
261impl FieldMiddleware for PassthroughMiddleware {
262 fn name(&self) -> &str {
263 "passthrough"
264 }
265
266 fn process(
267 &self,
268 ctx: &ResolverContext,
269 next: &dyn Fn(&ResolverContext) -> ResolverResult,
270 ) -> ResolverResult {
271 next(ctx)
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278 use serde_json::json;
279
280 fn make_resolver() -> FieldResolver {
281 let mut r = FieldResolver::new();
282 r.register("hello", |_ctx| Ok(json!("world")));
283 r.register("answer", |_ctx| Ok(json!(42)));
284 r.register("null_field", |_ctx| Ok(json!(null)));
285 r
286 }
287
288 #[test]
291 fn test_resolver_error_display_field_not_found() {
292 let e = ResolverError::FieldNotFound("foo".to_string());
293 assert!(e.to_string().contains("foo"));
294 }
295
296 #[test]
297 fn test_resolver_error_display_middleware() {
298 let e = ResolverError::MiddlewareError("oops".to_string());
299 assert!(e.to_string().contains("oops"));
300 }
301
302 #[test]
303 fn test_resolver_error_display_handler() {
304 let e = ResolverError::HandlerError("bad".to_string());
305 assert!(e.to_string().contains("bad"));
306 }
307
308 #[test]
309 fn test_resolver_error_is_error_trait() {
310 let e: Box<dyn std::error::Error> = Box::new(ResolverError::FieldNotFound("x".to_string()));
311 assert!(!e.to_string().is_empty());
312 }
313
314 #[test]
317 fn test_context_new() {
318 let ctx = ResolverContext::new("myField");
319 assert_eq!(ctx.field_name, "myField");
320 assert!(ctx.parent_value.is_none());
321 assert!(ctx.args.is_empty());
322 }
323
324 #[test]
325 fn test_context_with_parent() {
326 let ctx = ResolverContext::new("f").with_parent(json!({"id": 1}));
327 assert!(ctx.parent_value.is_some());
328 }
329
330 #[test]
331 fn test_context_with_arg() {
332 let ctx = ResolverContext::new("f").with_arg("limit", json!(10));
333 assert_eq!(ctx.args.get("limit"), Some(&json!(10)));
334 }
335
336 #[test]
337 fn test_context_clone() {
338 let ctx = ResolverContext::new("f").with_arg("k", json!("v"));
339 let ctx2 = ctx.clone();
340 assert_eq!(ctx2.field_name, "f");
341 }
342
343 #[test]
344 fn test_context_multiple_args() {
345 let ctx = ResolverContext::new("f")
346 .with_arg("a", json!(1))
347 .with_arg("b", json!(2));
348 assert_eq!(ctx.args.len(), 2);
349 }
350
351 #[test]
354 fn test_new_resolver_empty() {
355 let r = FieldResolver::new();
356 assert_eq!(r.middleware_count(), 0);
357 assert!(r.registered_fields().is_empty());
358 }
359
360 #[test]
361 fn test_default_resolver() {
362 let r = FieldResolver::default();
363 assert_eq!(r.middleware_count(), 0);
364 }
365
366 #[test]
367 fn test_register_field() {
368 let mut r = FieldResolver::new();
369 r.register("myField", |_| Ok(json!("ok")));
370 assert!(r.has_field("myField"));
371 }
372
373 #[test]
374 fn test_registered_fields_sorted() {
375 let mut r = FieldResolver::new();
376 r.register("z_field", |_| Ok(json!(1)));
377 r.register("a_field", |_| Ok(json!(2)));
378 r.register("m_field", |_| Ok(json!(3)));
379 let fields = r.registered_fields();
380 assert_eq!(fields, vec!["a_field", "m_field", "z_field"]);
381 }
382
383 #[test]
384 fn test_middleware_count() {
385 let mut r = FieldResolver::new();
386 r.add_middleware(Box::new(PassthroughMiddleware));
387 r.add_middleware(Box::new(PassthroughMiddleware));
388 assert_eq!(r.middleware_count(), 2);
389 }
390
391 #[test]
394 fn test_resolve_registered_field() {
395 let r = make_resolver();
396 let ctx = ResolverContext::new("hello");
397 let result = r.resolve(ctx).expect("should resolve");
398 assert_eq!(result, json!("world"));
399 }
400
401 #[test]
402 fn test_resolve_integer_field() {
403 let r = make_resolver();
404 let ctx = ResolverContext::new("answer");
405 let result = r.resolve(ctx).expect("should resolve");
406 assert_eq!(result, json!(42));
407 }
408
409 #[test]
410 fn test_resolve_null_field() {
411 let r = make_resolver();
412 let ctx = ResolverContext::new("null_field");
413 let result = r.resolve(ctx).expect("should resolve");
414 assert_eq!(result, json!(null));
415 }
416
417 #[test]
418 fn test_resolve_unknown_field_returns_error() {
419 let r = make_resolver();
420 let ctx = ResolverContext::new("nonexistent");
421 let err = r.resolve(ctx).expect_err("should fail");
422 assert!(matches!(err, ResolverError::FieldNotFound(_)));
423 }
424
425 #[test]
426 fn test_resolve_with_context_args() {
427 let mut r = FieldResolver::new();
428 r.register("greet", |ctx| {
429 let name = ctx
430 .args
431 .get("name")
432 .and_then(|v| v.as_str())
433 .unwrap_or("World");
434 Ok(json!(format!("Hello, {name}!")))
435 });
436 let ctx = ResolverContext::new("greet").with_arg("name", json!("Rust"));
437 let result = r.resolve(ctx).expect("ok");
438 assert_eq!(result, json!("Hello, Rust!"));
439 }
440
441 #[test]
442 fn test_resolve_handler_error_propagated() {
443 let mut r = FieldResolver::new();
444 r.register("bad", |_| Err(ResolverError::HandlerError("fail".into())));
445 let ctx = ResolverContext::new("bad");
446 let err = r.resolve(ctx).expect_err("should fail");
447 assert!(matches!(err, ResolverError::HandlerError(_)));
448 }
449
450 #[test]
453 fn test_passthrough_middleware_does_not_change_result() {
454 let mut r = make_resolver();
455 r.add_middleware(Box::new(PassthroughMiddleware));
456 let ctx = ResolverContext::new("hello");
457 let result = r.resolve(ctx).expect("ok");
458 assert_eq!(result, json!("world"));
459 }
460
461 #[test]
462 fn test_logging_middleware_transparent() {
463 let mut r = make_resolver();
464 r.add_middleware(Box::new(LoggingMiddleware::new("TEST")));
465 let ctx = ResolverContext::new("answer");
466 let result = r.resolve(ctx).expect("ok");
467 assert_eq!(result, json!(42));
468 }
469
470 #[test]
471 fn test_error_middleware_short_circuits() {
472 let mut r = make_resolver();
473 r.add_middleware(Box::new(ErrorMiddleware::new("blocked")));
474 let ctx = ResolverContext::new("hello");
475 let err = r.resolve(ctx).expect_err("should fail");
476 assert!(matches!(err, ResolverError::MiddlewareError(_)));
477 }
478
479 #[test]
480 fn test_middleware_chain_order() {
481 let mut r = make_resolver();
483 r.add_middleware(Box::new(PassthroughMiddleware));
484 r.add_middleware(Box::new(LoggingMiddleware::new("x")));
485 let ctx = ResolverContext::new("hello");
486 assert!(r.resolve(ctx).is_ok());
487 }
488
489 #[test]
490 fn test_middleware_name() {
491 let mw = LoggingMiddleware::new("test");
492 assert_eq!(mw.name(), "logging");
493
494 let emw = ErrorMiddleware::new("err");
495 assert_eq!(emw.name(), "error_injector");
496
497 let pmw = PassthroughMiddleware;
498 assert_eq!(pmw.name(), "passthrough");
499 }
500
501 #[test]
502 fn test_multiple_fields_resolved_correctly() {
503 let r = make_resolver();
504 let r1 = r.resolve(ResolverContext::new("hello")).expect("ok");
505 let r2 = r.resolve(ResolverContext::new("answer")).expect("ok");
506 assert_eq!(r1, json!("world"));
507 assert_eq!(r2, json!(42));
508 }
509
510 #[test]
511 fn test_resolve_with_parent_value() {
512 let mut r = FieldResolver::new();
513 r.register("id", |ctx| {
514 let id = ctx
515 .parent_value
516 .as_ref()
517 .and_then(|v| v.get("id"))
518 .cloned()
519 .unwrap_or(json!(0));
520 Ok(id)
521 });
522 let ctx = ResolverContext::new("id").with_parent(json!({"id": 99}));
523 let result = r.resolve(ctx).expect("ok");
524 assert_eq!(result, json!(99));
525 }
526
527 #[test]
528 fn test_error_before_middleware_not_reached() {
529 let mut r = FieldResolver::new();
531 r.add_middleware(Box::new(PassthroughMiddleware));
532 let ctx = ResolverContext::new("not_registered");
533 let err = r.resolve(ctx).expect_err("should fail");
534 assert!(matches!(err, ResolverError::FieldNotFound(_)));
535 }
536
537 #[test]
538 fn test_has_field_true() {
539 let r = make_resolver();
540 assert!(r.has_field("hello"));
541 assert!(r.has_field("answer"));
542 }
543
544 #[test]
545 fn test_has_field_false() {
546 let r = make_resolver();
547 assert!(!r.has_field("missing"));
548 }
549
550 #[test]
551 fn test_overwrite_handler() {
552 let mut r = FieldResolver::new();
553 r.register("f", |_| Ok(json!(1)));
554 r.register("f", |_| Ok(json!(2)));
555 let result = r.resolve(ResolverContext::new("f")).expect("ok");
556 assert_eq!(result, json!(2));
558 }
559
560 #[test]
561 fn test_many_middlewares_passthrough() {
562 let mut r = make_resolver();
563 for _ in 0..10 {
564 r.add_middleware(Box::new(PassthroughMiddleware));
565 }
566 assert_eq!(r.middleware_count(), 10);
567 let result = r.resolve(ResolverContext::new("hello")).expect("ok");
568 assert_eq!(result, json!("world"));
569 }
570
571 #[test]
572 fn test_resolver_field_not_found_message() {
573 let r = FieldResolver::new();
574 let err = r.resolve(ResolverContext::new("ghost")).expect_err("fail");
575 if let ResolverError::FieldNotFound(name) = err {
576 assert_eq!(name, "ghost");
577 } else {
578 panic!("Expected FieldNotFound");
579 }
580 }
581
582 #[test]
583 fn test_null_json_value_handler() {
584 let mut r = FieldResolver::new();
585 r.register("nullable", |_| Ok(serde_json::Value::Null));
586 let result = r.resolve(ResolverContext::new("nullable")).expect("ok");
587 assert!(result.is_null());
588 }
589
590 #[test]
591 fn test_array_json_value_handler() {
592 let mut r = FieldResolver::new();
593 r.register("list", |_| Ok(json!([1, 2, 3])));
594 let result = r.resolve(ResolverContext::new("list")).expect("ok");
595 assert_eq!(result, json!([1, 2, 3]));
596 }
597
598 #[test]
599 fn test_object_json_value_handler() {
600 let mut r = FieldResolver::new();
601 r.register("obj", |_| Ok(json!({"a": 1, "b": "two"})));
602 let result = r.resolve(ResolverContext::new("obj")).expect("ok");
603 assert_eq!(result["a"], json!(1));
604 }
605}