@@ -9,12 +9,17 @@ use std::collections::{BTreeMap, HashMap};
9
9
10
10
const IGNORED_KEYS : [ & str ; 3 ] = [ "metadata" , "apiVersion" , "kind" ] ;
11
11
12
+ #[ derive( Default ) ]
13
+ pub struct Config {
14
+ pub no_condition : bool ,
15
+ }
16
+
12
17
/// Scan a schema for structs and members, and recurse to find all structs
13
18
///
14
19
/// All found output structs will have its names prefixed by the kind it is for
15
- pub fn analyze ( schema : JSONSchemaProps , kind : & str ) -> Result < Output > {
20
+ pub fn analyze ( schema : JSONSchemaProps , kind : & str , cfg : Config ) -> Result < Output > {
16
21
let mut res = vec ! [ ] ;
17
- analyze_ ( & schema, "" , kind, 0 , & mut res) ?;
22
+ analyze_ ( & schema, "" , kind, 0 , & mut res, & cfg ) ?;
18
23
Ok ( Output ( res) )
19
24
}
20
25
@@ -31,6 +36,7 @@ fn analyze_(
31
36
stack : & str ,
32
37
level : u8 ,
33
38
results : & mut Vec < Container > ,
39
+ cfg : & Config ,
34
40
) -> Result < ( ) > {
35
41
let props = schema. properties . clone ( ) . unwrap_or_default ( ) ;
36
42
let mut array_recurse_level: HashMap < String , u8 > = Default :: default ( ) ;
@@ -46,7 +52,7 @@ fn analyze_(
46
52
if let Some ( extra_props) = & s. properties {
47
53
// map values is an object with properties
48
54
debug ! ( "Generating map struct for {} (under {})" , current, stack) ;
49
- let c = extract_container ( extra_props, stack, & mut array_recurse_level, level, schema) ?;
55
+ let c = extract_container ( extra_props, stack, & mut array_recurse_level, level, schema, cfg ) ?;
50
56
results. push ( c) ;
51
57
} else if !dict_type. is_empty ( ) {
52
58
warn ! ( "not generating type {} - using {} map" , current, dict_type) ;
@@ -60,7 +66,7 @@ fn analyze_(
60
66
warn ! ( "not generating type {} - using BTreeMap" , current) ;
61
67
return Ok ( ( ) ) ;
62
68
}
63
- let c = extract_container ( & props, stack, & mut array_recurse_level, level, schema) ?;
69
+ let c = extract_container ( & props, stack, & mut array_recurse_level, level, schema, cfg ) ?;
64
70
results. push ( c) ;
65
71
}
66
72
}
@@ -74,10 +80,10 @@ fn analyze_(
74
80
// again; additionalProperties XOR properties
75
81
let extras = if let Some ( JSONSchemaPropsOrBool :: Schema ( s) ) = schema. additional_properties . as_ref ( ) {
76
82
let extra_props = s. properties . clone ( ) . unwrap_or_default ( ) ;
77
- find_containers ( & extra_props, stack, & mut array_recurse_level, level, schema) ?
83
+ find_containers ( & extra_props, stack, & mut array_recurse_level, level, schema, cfg ) ?
78
84
} else {
79
85
// regular properties only
80
- find_containers ( & props, stack, & mut array_recurse_level, level, schema) ?
86
+ find_containers ( & props, stack, & mut array_recurse_level, level, schema, cfg ) ?
81
87
} ;
82
88
results. extend ( extras) ;
83
89
@@ -95,6 +101,7 @@ fn find_containers(
95
101
array_recurse_level : & mut HashMap < String , u8 > ,
96
102
level : u8 ,
97
103
schema : & JSONSchemaProps ,
104
+ cfg : & Config ,
98
105
) -> Result < Vec < Container > > {
99
106
//trace!("finding containers in: {}", serde_yaml::to_string(&props)?);
100
107
let mut results = vec ! [ ] ;
@@ -116,7 +123,7 @@ fn find_containers(
116
123
// unpack the inner object from the array wrap
117
124
if let Some ( JSONSchemaPropsOrArray :: Schema ( items) ) = & s. as_ref ( ) . items {
118
125
debug ! ( "..recursing into object member {}" , key) ;
119
- analyze_ ( items, & next_key, & next_stack, level + 1 , & mut results) ?;
126
+ analyze_ ( items, & next_key, & next_stack, level + 1 , & mut results, cfg ) ?;
120
127
handled_inner = true ;
121
128
}
122
129
}
@@ -130,7 +137,7 @@ fn find_containers(
130
137
}
131
138
if !handled_inner {
132
139
// normal object recurse
133
- analyze_ ( value, & next_key, & next_stack, level + 1 , & mut results) ?;
140
+ analyze_ ( value, & next_key, & next_stack, level + 1 , & mut results, cfg ) ?;
134
141
}
135
142
}
136
143
"array" => {
@@ -150,7 +157,7 @@ fn find_containers(
150
157
bail ! ( "could not recurse into vec" ) ;
151
158
}
152
159
}
153
- analyze_ ( & inner, & next_key, & next_stack, level + 1 , & mut results) ?;
160
+ analyze_ ( & inner, & next_key, & next_stack, level + 1 , & mut results, cfg ) ?;
154
161
}
155
162
}
156
163
"" => {
@@ -226,6 +233,7 @@ fn extract_container(
226
233
array_recurse_level : & mut HashMap < String , u8 > ,
227
234
level : u8 ,
228
235
schema : & JSONSchemaProps ,
236
+ cfg : & Config ,
229
237
) -> Result < Container , anyhow:: Error > {
230
238
let mut members = vec ! [ ] ;
231
239
//debug!("analyzing object {}", serde_json::to_string(&schema).unwrap());
@@ -262,9 +270,13 @@ fn extract_container(
262
270
"integer" => extract_integer_type ( value) ?,
263
271
"array" => {
264
272
// recurse through repeated arrays until we find a concrete type (keep track of how deep we went)
265
- let ( array_type, recurse_level) = array_recurse_for_type ( value, stack, key, 1 ) ?;
273
+ let ( mut array_type, recurse_level) = array_recurse_for_type ( value, stack, key, 1 ) ?;
266
274
trace ! ( "got array {} for {} in level {}" , array_type, key, recurse_level) ;
267
- array_recurse_level. insert ( key. clone ( ) , recurse_level) ;
275
+ if !cfg. no_condition && key == "conditions" && is_conditions ( value) {
276
+ array_type = "Vec<Condition>" . into ( ) ;
277
+ } else {
278
+ array_recurse_level. insert ( key. clone ( ) , recurse_level) ;
279
+ }
268
280
array_type
269
281
}
270
282
"" => {
@@ -439,6 +451,21 @@ fn array_recurse_for_type(
439
451
440
452
// ----------------------------------------------------------------------------
441
453
// helpers
454
+ fn is_conditions ( value : & JSONSchemaProps ) -> bool {
455
+ if let Some ( JSONSchemaPropsOrArray :: Schema ( props) ) = & value. items {
456
+ if let Some ( p) = & props. properties {
457
+ let type_ = p. get ( "type" ) ;
458
+ let status = p. get ( "status" ) ;
459
+ let reason = p. get ( "reason" ) ;
460
+ let message = p. get ( "message" ) ;
461
+ let ltt = p. get ( "lastTransitionTime" ) ;
462
+ if type_. is_some ( ) && status. is_some ( ) && reason. is_some ( ) && message. is_some ( ) && ltt. is_some ( ) {
463
+ return true ;
464
+ }
465
+ }
466
+ }
467
+ false
468
+ }
442
469
443
470
fn extract_date_type ( value : & JSONSchemaProps ) -> Result < String > {
444
471
Ok ( if let Some ( f) = & value. format {
@@ -499,7 +526,7 @@ fn extract_integer_type(value: &JSONSchemaProps) -> Result<String> {
499
526
// unit tests particular schema patterns
500
527
#[ cfg( test) ]
501
528
mod test {
502
- use crate :: analyze;
529
+ use super :: { analyze, Config as Cfg } ;
503
530
use k8s_openapi:: apiextensions_apiserver:: pkg:: apis:: apiextensions:: v1:: JSONSchemaProps ;
504
531
505
532
use std:: sync:: Once ;
@@ -545,7 +572,7 @@ mod test {
545
572
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
546
573
//println!("schema: {}", serde_json::to_string_pretty(&schema).unwrap());
547
574
548
- let structs = analyze ( schema, "Agent" ) . unwrap ( ) . 0 ;
575
+ let structs = analyze ( schema, "Agent" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
549
576
//println!("{:?}", structs);
550
577
let root = & structs[ 0 ] ;
551
578
assert_eq ! ( root. name, "Agent" ) ;
@@ -589,7 +616,7 @@ type: object
589
616
"# ;
590
617
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
591
618
//println!("schema: {}", serde_json::to_string_pretty(&schema).unwrap());
592
- let structs = analyze ( schema, "Server" ) . unwrap ( ) . 0 ;
619
+ let structs = analyze ( schema, "Server" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
593
620
//println!("{:#?}", structs);
594
621
595
622
let root = & structs[ 0 ] ;
@@ -620,7 +647,7 @@ type: object
620
647
"# ;
621
648
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
622
649
623
- let structs = analyze ( schema, "Server" ) . unwrap ( ) . 0 ;
650
+ let structs = analyze ( schema, "Server" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
624
651
let root = & structs[ 0 ] ;
625
652
assert_eq ! ( root. name, "Server" ) ;
626
653
// should have an IntOrString member:
@@ -646,7 +673,7 @@ type: object
646
673
type: object
647
674
"# ;
648
675
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
649
- let structs = analyze ( schema, "Options" ) . unwrap ( ) . 0 ;
676
+ let structs = analyze ( schema, "Options" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
650
677
println ! ( "got {:?}" , structs) ;
651
678
let root = & structs[ 0 ] ;
652
679
assert_eq ! ( root. name, "Options" ) ;
@@ -673,7 +700,7 @@ type: object
673
700
"# ;
674
701
675
702
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
676
- let structs = analyze ( schema, "MatchExpressions" ) . unwrap ( ) . 0 ;
703
+ let structs = analyze ( schema, "MatchExpressions" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
677
704
println ! ( "got {:?}" , structs) ;
678
705
let root = & structs[ 0 ] ;
679
706
assert_eq ! ( root. name, "MatchExpressions" ) ;
@@ -726,7 +753,7 @@ type: object
726
753
"# ;
727
754
728
755
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
729
- let structs = analyze ( schema, "Endpoint" ) . unwrap ( ) . 0 ;
756
+ let structs = analyze ( schema, "Endpoint" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
730
757
println ! ( "got {:?}" , structs) ;
731
758
let root = & structs[ 0 ] ;
732
759
assert_eq ! ( root. name, "Endpoint" ) ;
@@ -810,7 +837,7 @@ type: object
810
837
type: object"# ;
811
838
812
839
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
813
- let structs = analyze ( schema, "ServerSpec" ) . unwrap ( ) . 0 ;
840
+ let structs = analyze ( schema, "ServerSpec" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
814
841
println ! ( "got {:?}" , structs) ;
815
842
let root = & structs[ 0 ] ;
816
843
assert_eq ! ( root. name, "ServerSpec" ) ;
@@ -881,7 +908,7 @@ type: object
881
908
type: object
882
909
"# ;
883
910
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
884
- let structs = analyze ( schema, "ServiceMonitor" ) . unwrap ( ) . 0 ;
911
+ let structs = analyze ( schema, "ServiceMonitor" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
885
912
println ! ( "got {:?}" , structs) ;
886
913
let root = & structs[ 0 ] ;
887
914
assert_eq ! ( root. name, "ServiceMonitor" ) ;
@@ -944,7 +971,7 @@ type: object
944
971
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
945
972
946
973
//println!("schema: {}", serde_json::to_string_pretty(&schema).unwrap());
947
- let structs = analyze ( schema, "DestinationRule" ) . unwrap ( ) . 0 ;
974
+ let structs = analyze ( schema, "DestinationRule" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
948
975
//println!("{:#?}", structs);
949
976
950
977
// this should produce the root struct struct
@@ -979,7 +1006,7 @@ type: object
979
1006
"# ;
980
1007
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
981
1008
println ! ( "got schema {}" , serde_yaml:: to_string( & schema) . unwrap( ) ) ;
982
- let structs = analyze ( schema, "StatusCode" ) . unwrap ( ) . 0 ;
1009
+ let structs = analyze ( schema, "StatusCode" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
983
1010
println ! ( "got {:?}" , structs) ;
984
1011
let root = & structs[ 0 ] ;
985
1012
assert_eq ! ( root. name, "StatusCode" ) ;
@@ -1005,7 +1032,7 @@ type: object
1005
1032
"# ;
1006
1033
1007
1034
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
1008
- let structs = analyze ( schema, "KustomizationSpec" ) . unwrap ( ) . 0 ;
1035
+ let structs = analyze ( schema, "KustomizationSpec" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
1009
1036
println ! ( "got {:?}" , structs) ;
1010
1037
let root = & structs[ 0 ] ;
1011
1038
assert_eq ! ( root. name, "KustomizationSpec" ) ;
@@ -1047,7 +1074,7 @@ type: object
1047
1074
type: object
1048
1075
type: object"# ;
1049
1076
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
1050
- let structs = analyze ( schema, "AppProjectStatus" ) . unwrap ( ) . 0 ;
1077
+ let structs = analyze ( schema, "AppProjectStatus" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
1051
1078
println ! ( "got {:?}" , structs) ;
1052
1079
let root = & structs[ 0 ] ;
1053
1080
assert_eq ! ( root. name, "AppProjectStatus" ) ;
@@ -1081,7 +1108,7 @@ type: object
1081
1108
"# ;
1082
1109
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
1083
1110
1084
- let structs = analyze ( schema, "Agent" ) . unwrap ( ) . 0 ;
1111
+ let structs = analyze ( schema, "Agent" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
1085
1112
1086
1113
let root = & structs[ 0 ] ;
1087
1114
assert_eq ! ( root. name, "Agent" ) ;
@@ -1111,7 +1138,7 @@ type: object
1111
1138
"# ;
1112
1139
let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
1113
1140
1114
- let structs = analyze ( schema, "Geoip" ) . unwrap ( ) . 0 ;
1141
+ let structs = analyze ( schema, "Geoip" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
1115
1142
1116
1143
assert_eq ! ( structs. len( ) , 1 ) ;
1117
1144
assert_eq ! ( structs[ 0 ] . members. len( ) , 1 ) ;
@@ -1121,4 +1148,37 @@ type: object
1121
1148
"Option<Vec<BTreeMap<String, String>>>"
1122
1149
) ;
1123
1150
}
1151
+
1152
+ #[ test]
1153
+ fn uses_k8s_openapi_conditions ( ) {
1154
+ init ( ) ;
1155
+ let schema_str = r#"
1156
+ properties:
1157
+ conditions:
1158
+ items:
1159
+ properties:
1160
+ lastTransitionTime:
1161
+ type: string
1162
+ message:
1163
+ type: string
1164
+ observedGeneration:
1165
+ type: integer
1166
+ reason:
1167
+ type: string
1168
+ status:
1169
+ type: string
1170
+ type:
1171
+ type: string
1172
+ type: object
1173
+ type: array
1174
+ type: object
1175
+ "# ;
1176
+
1177
+ let schema: JSONSchemaProps = serde_yaml:: from_str ( schema_str) . unwrap ( ) ;
1178
+
1179
+ let structs = analyze ( schema, "Gateway" , Cfg :: default ( ) ) . unwrap ( ) . 0 ;
1180
+ assert_eq ! ( structs. len( ) , 1 ) ;
1181
+ assert_eq ! ( structs[ 0 ] . members. len( ) , 1 ) ;
1182
+ assert_eq ! ( structs[ 0 ] . members[ 0 ] . type_, "Option<Vec<Condition>>" ) ;
1183
+ }
1124
1184
}
0 commit comments