pincer_core/
param_meta.rs

1//! Parameter metadata for runtime access.
2//!
3//! This module provides types that allow middleware to inspect parameter
4//! information at runtime, useful for `OpenAPI` generation, validation,
5//! and debugging.
6
7use std::fmt;
8
9/// Parameter location in the HTTP request.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum ParamLocation {
12    /// Path parameter (e.g., `/users/{id}`)
13    Path,
14    /// Query parameter (e.g., `?limit=10`)
15    Query,
16    /// Header parameter
17    Header,
18    /// Request body (JSON)
19    Body,
20    /// Form data (URL-encoded or multipart)
21    Form,
22}
23
24impl fmt::Display for ParamLocation {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        match self {
27            Self::Path => write!(f, "path"),
28            Self::Query => write!(f, "query"),
29            Self::Header => write!(f, "header"),
30            Self::Body => write!(f, "body"),
31            Self::Form => write!(f, "form"),
32        }
33    }
34}
35
36/// Metadata about a single parameter.
37///
38/// This struct contains information about a method parameter that
39/// can be used for documentation, validation, or debugging.
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub struct ParamMeta {
42    /// The parameter name as declared in the method signature.
43    pub name: &'static str,
44    /// Where the parameter is sent in the HTTP request.
45    pub location: ParamLocation,
46    /// The Rust type name (e.g., "u64", "Option<String>").
47    pub type_name: &'static str,
48    /// Whether the parameter is required (not an Option type).
49    pub required: bool,
50}
51
52/// All parameter metadata for a method call.
53///
54/// This is stored in request extensions to allow middleware to access
55/// parameter information at runtime.
56///
57/// # Example
58///
59/// ```ignore
60/// // In middleware
61/// if let Some(meta) = request.extensions().get::<ParameterMetadata>() {
62///     println!("Method: {}", meta.method_name);
63///     for param in meta.parameters {
64///         println!("  {}: {} ({:?})", param.name, param.type_name, param.location);
65///     }
66/// }
67/// ```
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
69pub struct ParameterMetadata {
70    /// The method name that was called.
71    pub method_name: &'static str,
72    /// Metadata for each parameter.
73    pub parameters: &'static [ParamMeta],
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn param_location_display() {
82        assert_eq!(ParamLocation::Path.to_string(), "path");
83        assert_eq!(ParamLocation::Query.to_string(), "query");
84        assert_eq!(ParamLocation::Header.to_string(), "header");
85        assert_eq!(ParamLocation::Body.to_string(), "body");
86        assert_eq!(ParamLocation::Form.to_string(), "form");
87    }
88
89    #[test]
90    fn param_meta_construction() {
91        let meta = ParamMeta {
92            name: "id",
93            location: ParamLocation::Path,
94            type_name: "u64",
95            required: true,
96        };
97        assert_eq!(meta.name, "id");
98        assert_eq!(meta.location, ParamLocation::Path);
99        assert_eq!(meta.type_name, "u64");
100        assert!(meta.required);
101    }
102
103    #[test]
104    fn parameter_metadata_construction() {
105        static PARAMS: &[ParamMeta] = &[
106            ParamMeta {
107                name: "id",
108                location: ParamLocation::Path,
109                type_name: "u64",
110                required: true,
111            },
112            ParamMeta {
113                name: "limit",
114                location: ParamLocation::Query,
115                type_name: "Option<u32>",
116                required: false,
117            },
118        ];
119
120        let meta = ParameterMetadata {
121            method_name: "get_user",
122            parameters: PARAMS,
123        };
124
125        assert_eq!(meta.method_name, "get_user");
126        assert_eq!(meta.parameters.len(), 2);
127        assert_eq!(meta.parameters.first().expect("first param").name, "id");
128        assert_eq!(meta.parameters.get(1).expect("second param").name, "limit");
129    }
130
131    #[test]
132    fn parameter_metadata_default() {
133        let meta = ParameterMetadata::default();
134        assert_eq!(meta.method_name, "");
135        assert!(meta.parameters.is_empty());
136    }
137}