11use std:: collections:: { HashMap , HashSet } ;
22
3+ use itertools:: Itertools ;
34use matchit:: InsertError ;
45use swc_common:: errors:: HANDLER ;
56use swc_common:: Span ;
@@ -8,10 +9,14 @@ use crate::encore::parser::meta::v1;
89use crate :: legacymeta:: compute_meta;
910use crate :: parser:: parser:: { ParseContext , ParseResult } ;
1011use crate :: parser:: resources:: apis:: api:: { Endpoint , Method , Methods } ;
12+ use crate :: parser:: resources:: apis:: encoding:: { Param , ParamData } ;
1113use crate :: parser:: resources:: Resource ;
1214use crate :: parser:: respath:: Path ;
1315use crate :: parser:: types:: visitor:: VisitWith ;
14- use crate :: parser:: types:: { validation, visitor, ObjectId , ResolveState , Type , Validated } ;
16+ use crate :: parser:: types:: {
17+ validation, visitor, Basic , Custom , Interface , ObjectId , ResolveState , Type , Validated ,
18+ WireLocation , WireSpec ,
19+ } ;
1520use crate :: parser:: Range ;
1621use crate :: span_err:: ErrReporter ;
1722use litparser:: Sp ;
@@ -125,14 +130,99 @@ impl AppValidator<'_> {
125130 }
126131
127132 fn validate_endpoint ( & self , ep : & Endpoint ) {
133+ if let Some ( req_enc) = & ep. encoding . handshake {
134+ self . validate_req_params ( & req_enc. params ) ;
135+ }
136+ for req_enc in & ep. encoding . req {
137+ self . validate_req_params ( & req_enc. params ) ;
138+ }
139+ self . validate_resp_params ( & ep. encoding . resp . params ) ;
140+ if let Some ( schema) = & ep. encoding . raw_handshake_schema {
141+ self . validate_schema_type ( schema) ;
142+ self . validate_validations ( schema) ;
143+ }
128144 if let Some ( schema) = & ep. encoding . raw_req_schema {
145+ self . validate_schema_type ( schema) ;
129146 self . validate_validations ( schema) ;
130147 }
131148 if let Some ( schema) = & ep. encoding . raw_resp_schema {
149+ self . validate_schema_type ( schema) ;
132150 self . validate_validations ( schema) ;
133151 }
134152 }
135153
154+ fn validate_req_params ( & self , params : & Vec < Param > ) {
155+ for param in params {
156+ if let ParamData :: Query { .. } = param. loc {
157+ fn is_valid_query_type ( state : & ResolveState , typ : & Type ) -> bool {
158+ match resolve_to_concrete ( state, typ) {
159+ Type :: Basic ( _) | Type :: Literal ( _) => true ,
160+ Type :: Enum ( _) => true ,
161+ Type :: Array ( ref t) => is_valid_query_type ( state, & t. 0 ) ,
162+ Type :: Union ( ref u) => u. types . iter ( ) . all ( |t| is_valid_query_type ( state, t) ) ,
163+ Type :: Custom ( Custom :: Decimal ) => true ,
164+ Type :: Custom ( Custom :: WireSpec ( WireSpec {
165+ location : WireLocation :: Query ,
166+ underlying : typ,
167+ ..
168+ } ) ) => is_valid_query_type ( state, & typ) ,
169+ _ => false ,
170+ }
171+ }
172+
173+ if !is_valid_query_type ( self . pc . type_checker . state ( ) , & param. typ ) {
174+ HANDLER . with ( |handler| {
175+ handler. span_err ( param. range , "type not supported for query parameters" )
176+ } ) ;
177+ }
178+ } ;
179+ }
180+ }
181+
182+ fn validate_resp_params ( & self , params : & [ Param ] ) {
183+ let http_status_params: Vec < _ > = params
184+ . iter ( )
185+ . filter ( |p| matches ! ( p. loc, ParamData :: HTTPStatus ) )
186+ . sorted_by ( |a, b| a. range . cmp ( & b. range ) )
187+ . collect ( ) ;
188+
189+ if http_status_params. len ( ) > 1 {
190+ let first = http_status_params[ 0 ] ;
191+ HANDLER . with ( |handler| {
192+ let mut err = handler. struct_span_err (
193+ first. range ,
194+ "http status can only be defined once per response type" ,
195+ ) ;
196+
197+ for param in & http_status_params[ 1 ..] {
198+ err. span_note ( param. range , "also defined here" ) ;
199+ }
200+
201+ err. emit ( ) ;
202+ } ) ;
203+ }
204+ }
205+
206+ fn validate_schema_type ( & self , schema : & Sp < Type > ) {
207+ let state = self . pc . type_checker . state ( ) ;
208+ let concrete = resolve_to_concrete ( state, schema. get ( ) ) ;
209+
210+ let error_msg = match concrete {
211+ Type :: Interface ( Interface { index : Some ( _) , .. } ) => {
212+ Some ( "type index is not supported in schema types" )
213+ }
214+ Type :: Interface ( Interface { call : Some ( _) , .. } ) => {
215+ Some ( "call signatures are not supported in schema types" )
216+ }
217+ Type :: Interface ( _) | Type :: Basic ( Basic :: Void ) => None ,
218+ _ => Some ( "request and response types must be interfaces or void" ) ,
219+ } ;
220+
221+ if let Some ( msg) = error_msg {
222+ HANDLER . with ( |handler| handler. span_err ( schema. span ( ) , msg) ) ;
223+ }
224+ }
225+
136226 fn validate_validations ( & self , schema : & Sp < Type > ) {
137227 struct Visitor < ' a > {
138228 state : & ' a ResolveState ,
@@ -204,3 +294,15 @@ impl AppValidator<'_> {
204294 }
205295 }
206296}
297+
298+ fn resolve_to_concrete ( state : & ResolveState , typ : & Type ) -> Type {
299+ match typ {
300+ Type :: Optional ( opt) => resolve_to_concrete ( state, & opt. 0 ) ,
301+ Type :: Validated ( v) => resolve_to_concrete ( state, & v. typ ) ,
302+ Type :: Named ( named) => {
303+ let underlying = named. underlying ( state) ;
304+ resolve_to_concrete ( state, & underlying)
305+ }
306+ _ => typ. clone ( ) ,
307+ }
308+ }
0 commit comments