11#![ deny( clippy:: all) ]
22#![ deny( clippy:: pedantic) ]
33#![ forbid( unsafe_code) ]
4+ #![ allow( clippy:: unused_async) ]
45
56use std:: net:: SocketAddr ;
67
7- use axum:: { extract:: Multipart , http:: StatusCode , Json , Router } ;
8- use axum:: extract:: multipart:: MultipartError ;
98use axum:: response:: { IntoResponse , Response } ;
109use axum:: routing:: { get, post} ;
10+ use axum:: { http:: StatusCode , Json , Router } ;
1111use serde:: Serialize ;
1212
13+ mod headers;
14+ mod multipart;
15+
1316#[ tokio:: main]
1417async fn main ( ) {
15- // initialize tracing
1618 tracing_subscriber:: fmt:: init ( ) ;
1719
18- let app = Router :: new ( ) . route ( "/body/multipart" , post ( upload) ) . route ( "/openapi.json" , get ( openapi) ) ;
20+ let app = Router :: new ( )
21+ . route ( "/body/multipart" , post ( multipart:: upload) )
22+ . route ( "/parameters/header" , post ( headers:: main) )
23+ . route ( "/openapi.json" , get ( openapi) ) ;
1924 let addr = SocketAddr :: from ( ( [ 0 , 0 , 0 , 0 ] , 3000 ) ) ;
2025 tracing:: debug!( "listening on {}" , addr) ;
2126 axum:: Server :: bind ( & addr)
@@ -36,153 +41,39 @@ struct PublicError {
3641 missing_parameters : Vec < String > ,
3742}
3843
39- #[ derive( Debug , Serialize ) ]
40- struct Problem {
41- parameter_name : String ,
42- description : String ,
43- }
44-
45- impl IntoResponse for PublicError {
46- fn into_response ( self ) -> Response {
47- ( StatusCode :: BAD_REQUEST , Json ( self ) ) . into_response ( )
48- }
49- }
50-
51- impl From < MultipartError > for PublicError {
52- fn from ( e : MultipartError ) -> Self {
44+ impl PublicError {
45+ pub ( crate ) fn missing ( parameter : & str ) -> Self {
5346 PublicError {
54- errors : vec ! [ format! ( "Invalid multipart request: {}" , e . to_string ( ) ) ] ,
55- ..Default :: default ( )
47+ missing_parameters : vec ! [ String :: from ( parameter ) ] ,
48+ ..Self :: default ( )
5649 }
5750 }
58- }
59-
60- async fn upload ( mut multipart : Multipart ) -> Result < Json < File > , PublicError > {
61- let mut err: Option < PublicError > = None ;
62- let mut a_string = None ;
63- let mut description = None ;
64- let mut file_content_type = None ;
65- let mut file_name = None ;
66- let mut file_data = None ;
67-
68- while let Some ( field) = multipart. next_field ( ) . await ? {
69- let field_name = match field. name ( ) {
70- Some ( name) => name,
71- None => {
72- err. get_or_insert_with ( Default :: default)
73- . extra_parameters
74- . push ( String :: from ( "unnamed parameter" ) ) ;
75- continue ;
76- }
77- } ;
7851
79- match field_name {
80- "a_string" => {
81- if let Ok ( value) = field. text ( ) . await {
82- a_string = Some ( value) ;
83- } else {
84- err. get_or_insert_with ( Default :: default)
85- . invalid_parameters
86- . push ( Problem {
87- parameter_name : String :: from ( "a_string" ) ,
88- description : String :: from ( "must be a string" ) ,
89- } ) ;
90- }
91- }
92- "description" => {
93- if let Ok ( value) = field. text ( ) . await {
94- description = Some ( value) ;
95- } else {
96- err. get_or_insert_with ( Default :: default)
97- . invalid_parameters
98- . push ( Problem {
99- parameter_name : String :: from ( "description" ) ,
100- description : String :: from ( "must be a string" ) ,
101- } ) ;
102- }
103- }
104- "file" => {
105- if let Some ( value) = field. file_name ( ) {
106- file_name = Some ( value. to_string ( ) ) ;
107- } else {
108- err. get_or_insert_with ( Default :: default)
109- . invalid_parameters
110- . push ( Problem {
111- parameter_name : String :: from ( "file" ) ,
112- description : String :: from ( "must have a file name" ) ,
113- } ) ;
114- }
115-
116- if let Some ( value) = field. content_type ( ) {
117- file_content_type = Some ( value. to_string ( ) ) ;
118- } else {
119- err. get_or_insert_with ( Default :: default)
120- . invalid_parameters
121- . push ( Problem {
122- parameter_name : String :: from ( "file" ) ,
123- description : String :: from ( "must have a content type" ) ,
124- } ) ;
125- }
126-
127- if let Ok ( value) = field. bytes ( ) . await {
128- file_data = Some ( value) ;
129- } else {
130- err. get_or_insert_with ( Default :: default)
131- . invalid_parameters
132- . push ( Problem {
133- parameter_name : String :: from ( "file" ) ,
134- description : String :: from ( "must have data" ) ,
135- } ) ;
136- }
137- }
138- field_name => {
139- err. get_or_insert_with ( Default :: default)
140- . extra_parameters
141- . push ( String :: from ( field_name) ) ;
142- }
52+ pub ( crate ) fn invalid ( parameter_name : & str , description : & str ) -> Self {
53+ PublicError {
54+ invalid_parameters : vec ! [ Problem :: new( parameter_name, description) ] ,
55+ ..Self :: default ( )
14356 }
14457 }
58+ }
14559
146- let a_string = match a_string {
147- Some ( name) => name,
148- None => {
149- let mut err = err. unwrap_or_default ( ) ;
150- err. missing_parameters . push ( String :: from ( "a_string" ) ) ;
151- return Err ( err) ;
152- }
153- } ;
60+ #[ derive( Debug , Serialize ) ]
61+ struct Problem {
62+ parameter_name : String ,
63+ description : String ,
64+ }
15465
155- let ( file_content_type, file_name, file_data) = match ( file_content_type, file_name, file_data) {
156- ( Some ( file_content_type) , Some ( file_name) , Some ( file_data) ) => (
157- file_content_type. to_string ( ) ,
158- String :: from ( file_name) ,
159- file_data. to_vec ( ) ,
160- ) ,
161- _ => {
162- let mut err = err. unwrap_or_default ( ) ;
163- err. missing_parameters . push ( String :: from ( "file" ) ) ;
164- return Err ( err) ;
66+ impl Problem {
67+ pub ( crate ) fn new ( parameter_name : & str , description : & str ) -> Self {
68+ Problem {
69+ parameter_name : String :: from ( parameter_name) ,
70+ description : String :: from ( description) ,
16571 }
166- } ;
167-
168- if let Some ( err) = err {
169- Err ( err)
170- } else {
171- Ok ( Json ( File {
172- a_string,
173- description,
174- file_content_type,
175- file_name,
176- file_data : String :: from_utf8_lossy ( & file_data) . to_string ( ) ,
177- } ) )
17872 }
17973}
18074
181- #[ derive( Debug , Serialize ) ]
182- struct File {
183- a_string : String ,
184- description : Option < String > ,
185- file_content_type : String ,
186- file_name : String ,
187- file_data : String ,
75+ impl IntoResponse for PublicError {
76+ fn into_response ( self ) -> Response {
77+ ( StatusCode :: BAD_REQUEST , Json ( self ) ) . into_response ( )
78+ }
18879}
0 commit comments