salvo_oapi/extract/parameter/
path.rs1use std::fmt::{self, Debug, Formatter};
2use std::ops::{Deref, DerefMut};
3
4use salvo_core::extract::{Extractible, Metadata};
5use salvo_core::http::{ParseError, Request};
6use serde::{Deserialize, Deserializer};
7
8use crate::endpoint::EndpointArgRegister;
9use crate::{Components, Operation, Parameter, ParameterIn, ToSchema};
10
11pub struct PathParam<T>(pub T);
13impl<T> PathParam<T> {
14 pub fn into_inner(self) -> T {
16 self.0
17 }
18}
19
20impl<T> Deref for PathParam<T> {
21 type Target = T;
22
23 fn deref(&self) -> &Self::Target {
24 &self.0
25 }
26}
27
28impl<T> DerefMut for PathParam<T> {
29 fn deref_mut(&mut self) -> &mut Self::Target {
30 &mut self.0
31 }
32}
33
34impl<'de, T> Deserialize<'de> for PathParam<T>
35where
36 T: Deserialize<'de>,
37{
38 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
39 where
40 D: Deserializer<'de>,
41 {
42 T::deserialize(deserializer).map(|value| PathParam(value))
43 }
44}
45
46impl<T> fmt::Debug for PathParam<T>
47where
48 T: Debug,
49{
50 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
51 self.0.fmt(f)
52 }
53}
54
55impl<T> fmt::Display for PathParam<T>
56where
57 T: fmt::Display,
58{
59 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
60 self.0.fmt(f)
61 }
62}
63
64impl<'ex, T> Extractible<'ex> for PathParam<T>
65where
66 T: Deserialize<'ex>,
67{
68 fn metadata() -> &'ex Metadata {
69 static METADATA: Metadata = Metadata::new("");
70 &METADATA
71 }
72 #[allow(refining_impl_trait)]
73 async fn extract(_req: &'ex mut Request) -> Result<Self, ParseError> {
74 unimplemented!("path parameter can not be extracted from request")
75 }
76 #[allow(refining_impl_trait)]
77 async fn extract_with_arg(req: &'ex mut Request, arg: &str) -> Result<Self, ParseError> {
78 let value = req.param(arg).ok_or_else(|| {
79 ParseError::other(format!(
80 "path parameter {} not found or convert to type failed",
81 arg
82 ))
83 })?;
84 Ok(Self(value))
85 }
86}
87
88impl<T> EndpointArgRegister for PathParam<T>
89where
90 T: ToSchema,
91{
92 fn register(components: &mut Components, operation: &mut Operation, arg: &str) {
93 let parameter = Parameter::new(arg)
94 .parameter_in(ParameterIn::Path)
95 .description(format!("Get parameter `{arg}` from request url path."))
96 .schema(T::to_schema(components))
97 .required(true);
98 operation.parameters.insert(parameter);
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use assert_json_diff::assert_json_eq;
105 use salvo_core::test::TestClient;
106 use serde_json::json;
107
108 use super::*;
109
110 #[test]
111 fn test_path_param_into_inner() {
112 let param = PathParam::<String>("param".to_string());
113 assert_eq!("param".to_string(), param.into_inner());
114 }
115
116 #[test]
117 fn test_path_param_deref() {
118 let param = PathParam::<String>("param".to_string());
119 assert_eq!(&"param".to_string(), param.deref())
120 }
121
122 #[test]
123 fn test_path_param_deref_mut() {
124 let mut param = PathParam::<String>("param".to_string());
125 assert_eq!(&mut "param".to_string(), param.deref_mut())
126 }
127
128 #[test]
129 fn test_path_param_deserialize() {
130 let param = serde_json::from_str::<PathParam<String>>(r#""param""#).unwrap();
131 assert_eq!(param.0, "param");
132 }
133
134 #[test]
135 fn test_path_param_debug() {
136 let param = PathParam::<String>("param".to_string());
137 assert_eq!(format!("{:?}", param), r#""param""#);
138 }
139
140 #[test]
141 fn test_path_param_display() {
142 let param = PathParam::<String>("param".to_string());
143 assert_eq!(format!("{}", param), "param");
144 }
145
146 #[test]
147 fn test_path_param_metadata() {
148 let metadata = PathParam::<String>::metadata();
149 assert_eq!("", metadata.name);
150 }
151
152 #[tokio::test]
153 #[should_panic]
154 async fn test_path_prarm_extract() {
155 let mut req = Request::new();
156 let _ = PathParam::<String>::extract(&mut req).await;
157 }
158
159 #[tokio::test]
160 async fn test_path_prarm_extract_with_value() {
161 let req = TestClient::get("http://127.0.0.1:5801").build_hyper();
162 let schema = req.uri().scheme().cloned().unwrap();
163 let mut req = Request::from_hyper(req, schema);
164 req.params_mut().insert("param", "param".to_string());
165 let result = PathParam::<String>::extract_with_arg(&mut req, "param").await;
166 assert_eq!(result.unwrap().0, "param");
167 }
168
169 #[tokio::test]
170 #[should_panic]
171 async fn test_path_prarm_extract_with_value_panic() {
172 let req = TestClient::get("http://127.0.0.1:5801").build_hyper();
173 let schema = req.uri().scheme().cloned().unwrap();
174 let mut req = Request::from_hyper(req, schema);
175 let result = PathParam::<String>::extract_with_arg(&mut req, "param").await;
176 assert_eq!(result.unwrap().0, "param");
177 }
178
179 #[test]
180 fn test_path_param_register() {
181 let mut components = Components::new();
182 let mut operation = Operation::new();
183 PathParam::<String>::register(&mut components, &mut operation, "arg");
184
185 assert_json_eq!(
186 operation,
187 json!({
188 "parameters": [
189 {
190 "name": "arg",
191 "in": "path",
192 "description": "Get parameter `arg` from request url path.",
193 "required": true,
194 "schema": {
195 "type": "string"
196 }
197 }
198 ],
199 "responses": {}
200 })
201 )
202 }
203}