Skip to main content

apif_assert/
registry.rs

1use crate::engine::AssertionResult;
2use anyhow::Result;
3use serde_json::Value;
4use std::collections::HashMap;
5use std::sync::Arc;
6
7/// Context passed to plugins during assertion evaluation.
8#[derive(Debug, Clone)]
9pub struct PluginContext<'a> {
10    pub response: &'a Value,
11    pub headers: Option<&'a HashMap<String, String>>,
12    pub trailers: Option<&'a HashMap<String, String>>,
13    pub timing: Option<&'a AssertionTiming>,
14    /// Wire protocol that produced this response — `"grpc"`, `"grpc-web"`, or
15    /// `"connectrpc"` (the same canonical strings `OPTIONS.protocol:`
16    /// accepts). `None` when the caller didn't have protocol information to
17    /// give (e.g. a standalone/test evaluation with no real call behind it).
18    pub protocol: Option<&'a str>,
19}
20
21impl<'a> PluginContext<'a> {
22    pub fn new(response: &'a Value) -> Self {
23        Self {
24            response,
25            headers: None,
26            trailers: None,
27            timing: None,
28            protocol: None,
29        }
30    }
31    pub fn with_headers(mut self, headers: Option<&'a HashMap<String, String>>) -> Self {
32        self.headers = headers;
33        self
34    }
35    pub fn with_trailers(mut self, trailers: Option<&'a HashMap<String, String>>) -> Self {
36        self.trailers = trailers;
37        self
38    }
39    pub fn with_timing(mut self, timing: Option<&'a AssertionTiming>) -> Self {
40        self.timing = timing;
41        self
42    }
43    pub fn with_protocol(mut self, protocol: Option<&'a str>) -> Self {
44        self.protocol = protocol;
45        self
46    }
47}
48
49/// Timing context for assertion plugins.
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct AssertionTiming {
52    pub elapsed_ms: u64,
53    pub total_elapsed_ms: u64,
54    pub scope_message_count: usize,
55    pub scope_index: usize,
56}
57
58/// Result of a plugin execution.
59#[derive(Debug, Clone, PartialEq)]
60pub enum PluginResult {
61    Assertion(AssertionResult),
62    Value(Value),
63}
64
65/// Minimal plugin API — just what the assertion engine needs.
66pub trait PluginApi: Send + Sync {
67    fn execute(&self, args: &[Value], context: &PluginContext) -> Result<PluginResult>;
68}
69
70/// Registry of plugins for the assertion engine.
71pub trait PluginRegistry: Send + Sync {
72    fn get_plugin(&self, name: &str) -> Option<Arc<dyn PluginApi>>;
73}
74
75/// A plugin registry that has no plugins. Used as default when no plugins are configured.
76pub struct NoopPluginRegistry;
77
78impl PluginRegistry for NoopPluginRegistry {
79    fn get_plugin(&self, _name: &str) -> Option<Arc<dyn PluginApi>> {
80        None
81    }
82}