1+ use proc_macro2:: TokenStream ;
2+ use quote:: { ToTokens , quote} ;
13use syn:: { GenericArgument , Path , PathArguments , Type , TypeParamBound } ;
24
5+ use crate :: validate:: field:: format_error_ident;
6+
37const PRIMITIVE_AND_BUILT_IN_TYPES : [ & str ; 18 ] = [
48 "bool" , "i8" , "i16" , "i32" , "i64" , "i128" , "isize" , "u8" , "u16" , "u32" , "u64" , "u128" , "usize" ,
59 "f32" , "f64" , "char" , "str" , "String" ,
610] ;
711
8- const CONTAINER_TYPES : [ & str ; 20 ] = [
12+ const INDEXED_CONTAINER_TYPES : [ & str ; 16 ] = [
913 "Arc" ,
10- "BTreeMap" ,
1114 "BTreeSet" ,
12- "HashMap" ,
1315 "HashSet" ,
1416 "LinkedList" ,
1517 "Option" ,
1618 "Rc" ,
1719 "Vec" ,
1820 "VecDeque" ,
19- "std::collections::BTreeMap" ,
2021 "std::collections::BTreeSet" ,
21- "std::collections::HashMap" ,
2222 "std::collections::HashSet" ,
2323 "std::collections::LinkedList" ,
2424 "std::collections::VecDeque" ,
@@ -28,6 +28,13 @@ const CONTAINER_TYPES: [&str; 20] = [
2828 "std::vec::Vec" ,
2929] ;
3030
31+ const KEYED_CONTAINER_TYPES : [ & str ; 4 ] = [
32+ "BTreeMap" ,
33+ "HashMap" ,
34+ "std::collections::BTreeMap" ,
35+ "std::collections::HashMap" ,
36+ ] ;
37+
3138fn path_to_string ( path : & Path ) -> String {
3239 // TODO: This is probably slow, replace with comparisons.
3340 path. segments
@@ -42,59 +49,88 @@ fn is_validate_path(path: &Path) -> bool {
4249 path_string == "Validate" || path_string == "fortifier::Validate"
4350}
4451
45- fn should_validate_generic_argument ( arg : & GenericArgument ) -> bool {
52+ fn should_validate_generic_argument ( arg : & GenericArgument ) -> Option < KnownOrUnknown < TokenStream > > {
4653 match arg {
47- GenericArgument :: Lifetime ( _) => true ,
54+ GenericArgument :: Lifetime ( _) => Some ( KnownOrUnknown :: Unknown ) ,
4855 GenericArgument :: Type ( r#type) => should_validate_type ( r#type) ,
4956 GenericArgument :: Const ( _expr) => todo ! ( ) ,
5057 GenericArgument :: AssocType ( _assoc_type) => todo ! ( ) ,
5158 GenericArgument :: AssocConst ( _assoc_const) => todo ! ( ) ,
5259 GenericArgument :: Constraint ( _constraint) => todo ! ( ) ,
53- _ => true ,
60+ _ => Some ( KnownOrUnknown :: Unknown ) ,
5461 }
5562}
5663
57- fn should_validate_path ( path : & Path ) -> bool {
64+ fn should_validate_path ( path : & Path ) -> Option < KnownOrUnknown < TokenStream > > {
5865 if let Some ( ident) = path. get_ident ( ) {
59- return !PRIMITIVE_AND_BUILT_IN_TYPES . contains ( & ident. to_string ( ) . as_str ( ) ) ;
66+ return if PRIMITIVE_AND_BUILT_IN_TYPES . contains ( & ident. to_string ( ) . as_str ( ) ) {
67+ None
68+ } else {
69+ Some ( KnownOrUnknown :: Known (
70+ format_error_ident ( ident) . to_token_stream ( ) ,
71+ ) )
72+ } ;
6073 }
6174 let path_string = path_to_string ( path) ;
75+ let path_string = path_string. as_str ( ) ;
6276
63- if CONTAINER_TYPES . contains ( & path_string. as_str ( ) )
77+ if INDEXED_CONTAINER_TYPES . contains ( & path_string)
6478 && let Some ( segment) = path. segments . last ( )
6579 && let PathArguments :: AngleBracketed ( arguments) = & segment. arguments
66- && ! arguments. args . iter ( ) . all ( should_validate_generic_argument )
80+ && let Some ( argument ) = arguments. args . first ( )
6781 {
68- return false ;
82+ return should_validate_generic_argument ( argument) . map ( |error_type| match error_type {
83+ KnownOrUnknown :: Known ( error_type) => KnownOrUnknown :: Known (
84+ quote ! ( :: fortifier:: ValidationErrors <:: fortifier:: IndexedValidationError <#error_type>>)
85+ ) ,
86+ KnownOrUnknown :: Unknown => KnownOrUnknown :: Unknown
87+ } ) ;
6988 }
7089
71- true
90+ // TODO: Determine error type.
91+ if KEYED_CONTAINER_TYPES . contains ( & path_string)
92+ && let Some ( segment) = path. segments . last ( )
93+ && let PathArguments :: AngleBracketed ( arguments) = & segment. arguments
94+ && !arguments
95+ . args
96+ . iter ( )
97+ . all ( |arg| should_validate_generic_argument ( arg) . is_some ( ) )
98+ {
99+ return None ;
100+ }
101+
102+ Some ( KnownOrUnknown :: Unknown )
103+ }
104+
105+ pub enum KnownOrUnknown < T > {
106+ Known ( T ) ,
107+ Unknown ,
72108}
73109
74- pub fn should_validate_type ( r#type : & Type ) -> bool {
110+ pub fn should_validate_type ( r#type : & Type ) -> Option < KnownOrUnknown < TokenStream > > {
75111 match r#type {
76112 Type :: Array ( r#type) => should_validate_type ( & r#type. elem ) ,
77- Type :: BareFn ( _) => false ,
113+ Type :: BareFn ( _) => None ,
78114 Type :: Group ( r#type) => should_validate_type ( & r#type. elem ) ,
79115 Type :: ImplTrait ( r#type) => r#type. bounds . iter ( ) . any (
80116 |bound| matches ! ( bound, TypeParamBound :: Trait ( bound) if is_validate_path( & bound. path) ) ,
81- ) ,
82- Type :: Infer ( _) => true ,
83- Type :: Macro ( _) => true ,
84- Type :: Never ( _) => false ,
117+ ) . then_some ( KnownOrUnknown :: Unknown ) ,
118+ Type :: Infer ( _) => Some ( KnownOrUnknown :: Unknown ) ,
119+ Type :: Macro ( _) => Some ( KnownOrUnknown :: Unknown ) ,
120+ Type :: Never ( _) => None ,
85121 Type :: Paren ( r#type) => should_validate_type ( & r#type. elem ) ,
86122 Type :: Path ( r#type) => should_validate_path ( & r#type. path ) ,
87123 Type :: Ptr ( r#type) => should_validate_type ( & r#type. elem ) ,
88124 Type :: Reference ( r#type) => should_validate_type ( & r#type. elem ) ,
89125 Type :: Slice ( r#type) => should_validate_type ( & r#type. elem ) ,
90126 Type :: TraitObject ( r#type) => r#type. bounds . iter ( ) . any (
91127 |bound| matches ! ( bound, TypeParamBound :: Trait ( bound) if is_validate_path( & bound. path) ) ,
92- ) ,
128+ ) . then_some ( KnownOrUnknown :: Unknown ) ,
93129 Type :: Tuple ( r#type) => {
94- !r#type. elems . is_empty ( ) && r#type. elems . iter ( ) . all ( should_validate_type)
130+ ( !r#type. elems . is_empty ( ) && r#type. elems . iter ( ) . all ( |r#type| should_validate_type ( r#type ) . is_some ( ) ) ) . then_some ( KnownOrUnknown :: Unknown )
95131 }
96- Type :: Verbatim ( _) => false ,
97- _ => false ,
132+ Type :: Verbatim ( _) => None ,
133+ _ => None ,
98134 }
99135}
100136
@@ -106,7 +142,7 @@ mod tests {
106142 use super :: should_validate_type;
107143
108144 fn validate ( tokens : TokenStream ) -> bool {
109- should_validate_type ( & syn:: parse2 ( tokens) . expect ( "valid type" ) )
145+ should_validate_type ( & syn:: parse2 ( tokens) . expect ( "valid type" ) ) . is_some ( )
110146 }
111147
112148 #[ test]
0 commit comments