@@ -19,9 +19,10 @@ use crate::error::Error;
19
19
#[ derive( Debug , Clone , Serialize , JsonSchema ) ]
20
20
pub struct Baseline < ' a > {
21
21
#[ serde( flatten) ]
22
- pub support : & ' a SupportStatusWithByKey ,
22
+ pub support : & ' a SupportStatus ,
23
23
#[ serde( skip_serializing_if = "std::ops::Not::not" ) ]
24
24
pub asterisk : bool ,
25
+ pub feature : & ' a FeatureData ,
25
26
}
26
27
27
28
#[ derive( Deserialize , Serialize , Clone , Debug ) ]
@@ -32,6 +33,7 @@ pub struct WebFeatures {
32
33
33
34
#[ derive( Deserialize , Serialize , Clone , Debug ) ]
34
35
pub struct KeyStatus {
36
+ bcd_key_spaced : String ,
35
37
bcd_key : String ,
36
38
feature : String ,
37
39
}
@@ -46,11 +48,6 @@ fn spaced(bcd_key: &str) -> String {
46
48
bcd_key. replace ( '.' , " " )
47
49
}
48
50
49
- #[ inline]
50
- fn unspaced ( bcd_key : & str ) -> String {
51
- bcd_key. replace ( ' ' , "." )
52
- }
53
-
54
51
impl WebFeatures {
55
52
pub fn from_file ( path : & Path ) -> Result < Self , Error > {
56
53
let json_str = read_to_string ( path) ?;
@@ -86,28 +83,29 @@ impl WebFeatures {
86
83
. iter ( )
87
84
. flat_map ( |( feature, fd) | {
88
85
fd. compat_features . iter ( ) . map ( |bcd_key| KeyStatus {
89
- bcd_key : spaced ( bcd_key) ,
86
+ bcd_key : bcd_key. clone ( ) ,
87
+ bcd_key_spaced : spaced ( bcd_key) ,
90
88
feature : feature. clone ( ) ,
91
89
} )
92
90
} )
93
91
. collect ( ) ;
94
- bcd_keys. sort_by ( |a, b| a. bcd_key . cmp ( & b. bcd_key ) ) ;
95
- bcd_keys. dedup_by ( |a, b| a. bcd_key == b. bcd_key ) ;
92
+ bcd_keys. sort_by ( |a, b| a. bcd_key_spaced . cmp ( & b. bcd_key_spaced ) ) ;
93
+ bcd_keys. dedup_by ( |a, b| a. bcd_key_spaced == b. bcd_key_spaced ) ;
96
94
97
95
let map = WebFeatures { features, bcd_keys } ;
98
96
Ok ( map)
99
97
}
100
98
101
- pub fn sub_keys ( & self , bcd_key : & str ) -> & [ KeyStatus ] {
102
- let suffix = concat_strs ! ( bcd_key , " " ) ;
99
+ pub fn sub_keys ( & self , bcd_key_spaced : & str ) -> & [ KeyStatus ] {
100
+ let suffix = concat_strs ! ( bcd_key_spaced , " " ) ;
103
101
if let Ok ( start) = self
104
102
. bcd_keys
105
- . binary_search_by_key ( & bcd_key , |ks| & ks. bcd_key )
103
+ . binary_search_by_key ( & bcd_key_spaced , |ks| & ks. bcd_key_spaced )
106
104
{
107
105
if start < self . bcd_keys . len ( ) {
108
106
if let Some ( end) = self . bcd_keys [ start + 1 ..]
109
107
. iter ( )
110
- . position ( |ks| !ks. bcd_key . starts_with ( & suffix) )
108
+ . position ( |ks| !ks. bcd_key_spaced . starts_with ( & suffix) )
111
109
{
112
110
return & self . bcd_keys [ start + 1 ..start + 1 + end] ;
113
111
}
@@ -118,154 +116,120 @@ impl WebFeatures {
118
116
119
117
// Compute status according to:
120
118
// https://github.com/mdn/yari/issues/11546#issuecomment-2531611136
121
- pub fn feature_status ( & self , bcd_key : & str ) -> Option < Baseline > {
119
+ pub fn baseline_by_bcd_key ( & self , bcd_key : & str ) -> Option < Baseline > {
122
120
let bcd_key_spaced = & spaced ( bcd_key) ;
123
- if let Some ( status) = self . feature_status_internal ( bcd_key_spaced) {
124
- let sub_keys = self . sub_keys ( bcd_key_spaced) ;
125
- let sub_status = sub_keys
126
- . iter ( )
127
- . map ( |sub_key| {
128
- self . feature_status_internal_with_feature_name (
129
- & sub_key. bcd_key ,
130
- & sub_key. feature ,
131
- )
132
- . and_then ( |status| status. baseline )
133
- } )
134
- . collect :: < Vec < _ > > ( ) ;
135
-
136
- if sub_status
137
- . iter ( )
138
- . all ( |baseline| baseline == & status. baseline )
139
- {
140
- return Some ( Baseline {
141
- support : status,
142
- asterisk : false ,
143
- } ) ;
144
- }
145
- match status. baseline {
146
- Some ( BaselineHighLow :: False ) => {
147
- let Support {
148
- chrome,
149
- chrome_android,
150
- firefox,
151
- firefox_android,
152
- safari,
153
- safari_ios,
154
- ..
155
- } = & status. support ;
156
- if chrome == chrome_android
157
- && firefox == firefox_android
158
- && safari == safari_ios
159
- {
160
- return Some ( Baseline {
161
- support : status,
162
- asterisk : false ,
163
- } ) ;
164
- }
165
- }
166
- Some ( BaselineHighLow :: Low ) => {
167
- if sub_status
121
+ if let Some ( feature) = self . feature_data_by_key ( bcd_key_spaced) {
122
+ if let Some ( status) = feature. status . as_ref ( ) {
123
+ if let Some ( status_for_key) = status
124
+ . by_compat_key
125
+ . as_ref ( )
126
+ . and_then ( |by_key| by_key. get ( bcd_key) )
127
+ {
128
+ let sub_keys = self . sub_keys ( bcd_key_spaced) ;
129
+ let sub_status = sub_keys
168
130
. iter ( )
169
- . all ( |ss| matches ! ( ss, Some ( BaselineHighLow :: Low | BaselineHighLow :: High ) ) )
131
+ . map ( |sub_key| {
132
+ self . feature_data_by_name ( & sub_key. feature )
133
+ . and_then ( |feature| feature. status . as_ref ( ) )
134
+ . and_then ( |status| status. by_compat_key . as_ref ( ) )
135
+ . and_then ( |by_key| by_key. get ( & sub_key. bcd_key ) )
136
+ . and_then ( |status_for_key| status_for_key. baseline )
137
+ } )
138
+ . collect :: < Vec < _ > > ( ) ;
139
+
140
+ let asterisk = if sub_status
141
+ . iter ( )
142
+ . all ( |baseline| baseline == & status. baseline )
170
143
{
171
- return Some ( Baseline {
172
- support : status,
173
- asterisk : false ,
174
- } ) ;
175
- }
144
+ false
145
+ } else {
146
+ match status. baseline {
147
+ Some ( BaselineHighLow :: False ) => {
148
+ let Support {
149
+ chrome,
150
+ chrome_android,
151
+ firefox,
152
+ firefox_android,
153
+ safari,
154
+ safari_ios,
155
+ ..
156
+ } = & status. support ;
157
+ !( chrome == chrome_android
158
+ && firefox == firefox_android
159
+ && safari == safari_ios)
160
+ }
161
+ Some ( BaselineHighLow :: Low ) => !sub_status. iter ( ) . all ( |ss| {
162
+ matches ! ( ss, Some ( BaselineHighLow :: Low | BaselineHighLow :: High ) )
163
+ } ) ,
164
+ _ => true ,
165
+ }
166
+ } ;
167
+ return Some ( Baseline {
168
+ support : status_for_key,
169
+ asterisk,
170
+ feature,
171
+ } ) ;
176
172
}
177
- _ => { }
178
173
}
179
- Some ( Baseline {
180
- support : status,
181
- asterisk : true ,
182
- } )
183
- } else {
184
- None
185
174
}
175
+ None
186
176
}
187
177
188
- fn feature_status_internal ( & self , bcd_key_spaced : & str ) -> Option < & SupportStatusWithByKey > {
178
+ fn feature_data_by_key ( & self , bcd_key_spaced : & str ) -> Option < & FeatureData > {
189
179
if let Ok ( i) = self
190
180
. bcd_keys
191
- . binary_search_by ( |ks| ks. bcd_key . as_str ( ) . cmp ( bcd_key_spaced) )
181
+ . binary_search_by ( |ks| ks. bcd_key_spaced . as_str ( ) . cmp ( bcd_key_spaced) )
192
182
{
193
183
let feature_name = & self . bcd_keys [ i] . feature ;
194
- return self . feature_status_internal_with_feature_name ( bcd_key_spaced , feature_name) ;
184
+ return self . feature_data_by_name ( feature_name) ;
195
185
}
196
186
None
197
187
}
198
188
199
- fn feature_status_internal_with_feature_name (
200
- & self ,
201
- bcd_key_spaced : & str ,
202
- feature_name : & str ,
203
- ) -> Option < & SupportStatusWithByKey > {
189
+ fn feature_data_by_name ( & self , feature_name : & str ) -> Option < & FeatureData > {
204
190
if let Some ( feature_data) = self . features . get ( feature_name) {
205
191
if feature_data. discouraged . is_some ( ) {
206
192
return None ;
207
193
}
208
- if let Some ( ref status) = feature_data. status {
209
- if let Some ( by_key) = & status. by_compat_key {
210
- if let Some ( key_status) = by_key. get ( & unspaced ( bcd_key_spaced) ) {
211
- if key_status. baseline == status. baseline {
212
- return Some ( status) ;
213
- }
214
- }
215
- }
216
- }
194
+ return Some ( feature_data) ;
217
195
}
218
196
None
219
197
}
220
198
}
221
199
222
- #[ derive( Deserialize , Serialize , Clone , Debug ) ]
200
+ #[ derive( Deserialize , Serialize , Clone , Debug , JsonSchema ) ]
223
201
pub struct FeatureData {
224
202
/** Specification */
225
- #[ serde(
226
- deserialize_with = "t_or_vec" ,
227
- default ,
228
- skip_serializing_if = "Vec::is_empty"
229
- ) ]
203
+ #[ serde( deserialize_with = "t_or_vec" , default , skip_serializing) ]
230
204
pub spec : Vec < Url > ,
231
205
/** caniuse.com identifier */
232
- #[ serde(
233
- deserialize_with = "t_or_vec" ,
234
- default ,
235
- skip_serializing_if = "Vec::is_empty"
236
- ) ]
206
+ #[ serde( deserialize_with = "t_or_vec" , default , skip_serializing) ]
237
207
pub caniuse : Vec < String > ,
238
208
/** Whether a feature is considered a "baseline" web platform feature and when it achieved that status */
239
209
#[ serde( skip_serializing_if = "Option::is_none" ) ]
240
210
pub status : Option < SupportStatusWithByKey > ,
241
211
/** Sources of support data for this feature */
242
- #[ serde(
243
- deserialize_with = "t_or_vec" ,
244
- default ,
245
- skip_serializing_if = "Vec::is_empty"
246
- ) ]
212
+ #[ serde( deserialize_with = "t_or_vec" , default , skip_serializing) ]
247
213
pub compat_features : Vec < String > ,
214
+ #[ serde( skip_serializing) ]
248
215
pub description : String ,
249
216
pub description_html : String ,
250
217
#[ serde(
251
218
deserialize_with = "t_or_vec" ,
252
219
default ,
253
220
skip_serializing_if = "Vec::is_empty"
254
221
) ]
222
+ #[ serde( skip_serializing) ]
255
223
pub group : Vec < String > ,
256
224
pub name : String ,
257
- #[ serde(
258
- deserialize_with = "t_or_vec" ,
259
- default ,
260
- skip_serializing_if = "Vec::is_empty"
261
- ) ]
225
+ #[ serde( deserialize_with = "t_or_vec" , default , skip_serializing) ]
262
226
pub snapshot : Vec < String > ,
263
227
/** Whether developers are formally discouraged from using this feature */
264
228
#[ serde( skip_serializing_if = "Option::is_none" ) ]
265
229
pub discouraged : Option < Discouraged > ,
266
230
}
267
231
268
- #[ derive( Deserialize , Serialize , Clone , Debug ) ]
232
+ #[ derive( Deserialize , Serialize , Clone , Debug , JsonSchema ) ]
269
233
pub struct Discouraged {
270
234
#[ serde( default , skip_serializing_if = "Vec::is_empty" ) ]
271
235
according_to : Vec < String > ,
0 commit comments