Skip to content

Commit 83a402d

Browse files
authored
feat(baseline): display baseline by bcd key (#200)
* feat(baseline): display baseline by bcd key * rename and fix by key status
1 parent 3d3192e commit 83a402d

File tree

7 files changed

+82
-133
lines changed

7 files changed

+82
-133
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ tests/data/translated_content/files/*
1212
# Must be top level so these are included in npm publish.
1313
rari-npm/schema.json
1414
rari-npm/lib/rari-types.d.ts
15+
16+
# inline deps
17+
/.deps

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ console = "0.15"
9494
sha2 = "0.10"
9595
dashmap = { version = "6", features = ["serde"] }
9696
serde_yaml_ng = "0.10"
97-
schemars = { version = "0.8", features = ["chrono"] }
97+
schemars = { version = "0.8", features = ["chrono", "url"] }
9898
pretty_yaml = "0.5"
9999
yaml_parser = "0.2"
100100
const_format = "0.2"

crates/diff-test/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,5 @@ html-minifier = "5"
2222
ansi-to-html = "0.2"
2323
similar = "2"
2424
quick-xml = "0.37"
25-
sha2 = "0.10"
2625
clap = { version = "4", features = ["derive"] }
27-
dashmap = "6"
2826
lol_html = "2"

crates/diff-test/src/main.rs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ use std::io::{BufWriter, Write as _};
88
use std::path::Path;
99
use std::sync::atomic::AtomicUsize;
1010
use std::sync::atomic::Ordering::Relaxed;
11-
use std::sync::{Arc, LazyLock};
11+
use std::sync::LazyLock;
1212

1313
use anyhow::{anyhow, Error};
14-
use base64::prelude::{Engine as _, BASE64_STANDARD_NO_PAD};
1514
use clap::{Args, Parser, Subcommand};
16-
use dashmap::DashMap;
1715
use ignore::types::TypesBuilder;
1816
use ignore::WalkBuilder;
1917
use itertools::Itertools;
@@ -23,7 +21,6 @@ use prettydiff::{diff_lines, diff_words};
2321
use rayon::prelude::*;
2422
use regex::Regex;
2523
use serde_json::Value;
26-
use sha2::{Digest, Sha256};
2724
use xml::fmt_html;
2825

2926
mod xml;
@@ -203,9 +200,6 @@ static WS_DIFF: LazyLock<Regex> =
203200

204201
static EMPTY_P_DIFF: LazyLock<Regex> = LazyLock::new(|| Regex::new(r#"<p>[\n ]*</p>"#).unwrap());
205202

206-
static DIFF_MAP: LazyLock<Arc<DashMap<String, String>>> =
207-
LazyLock::new(|| Arc::new(DashMap::new()));
208-
209203
/// Run html content through these handlers to clean up the html before minifying and diffing.
210204
fn pre_diff_element_massaging_handlers<'a>(
211205
_args: &BuildArgs,
@@ -353,15 +347,6 @@ fn full_diff(
353347
rhs = fmt_html(&html_minifier::minify(rhs_t).unwrap());
354348
}
355349
if lhs != rhs {
356-
let mut diff_hash = Sha256::new();
357-
diff_hash.write_all(lhs.as_bytes()).unwrap();
358-
diff_hash.write_all(rhs.as_bytes()).unwrap();
359-
let diff_hash = BASE64_STANDARD_NO_PAD.encode(&diff_hash.finalize()[..]);
360-
if let Some(hash) = DIFF_MAP.get(&diff_hash) {
361-
diff.insert(key, format!("See {}", hash.as_str()));
362-
return;
363-
}
364-
DIFF_MAP.insert(diff_hash, "somewhere else".into());
365350
diff.insert(
366351
key,
367352
ansi_to_html::convert(&if args.fast {

crates/rari-data/src/baseline.rs

Lines changed: 75 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ use crate::error::Error;
1919
#[derive(Debug, Clone, Serialize, JsonSchema)]
2020
pub struct Baseline<'a> {
2121
#[serde(flatten)]
22-
pub support: &'a SupportStatusWithByKey,
22+
pub support: &'a SupportStatus,
2323
#[serde(skip_serializing_if = "std::ops::Not::not")]
2424
pub asterisk: bool,
25+
pub feature: &'a FeatureData,
2526
}
2627

2728
#[derive(Deserialize, Serialize, Clone, Debug)]
@@ -32,6 +33,7 @@ pub struct WebFeatures {
3233

3334
#[derive(Deserialize, Serialize, Clone, Debug)]
3435
pub struct KeyStatus {
36+
bcd_key_spaced: String,
3537
bcd_key: String,
3638
feature: String,
3739
}
@@ -46,11 +48,6 @@ fn spaced(bcd_key: &str) -> String {
4648
bcd_key.replace('.', " ")
4749
}
4850

49-
#[inline]
50-
fn unspaced(bcd_key: &str) -> String {
51-
bcd_key.replace(' ', ".")
52-
}
53-
5451
impl WebFeatures {
5552
pub fn from_file(path: &Path) -> Result<Self, Error> {
5653
let json_str = read_to_string(path)?;
@@ -86,28 +83,29 @@ impl WebFeatures {
8683
.iter()
8784
.flat_map(|(feature, fd)| {
8885
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),
9088
feature: feature.clone(),
9189
})
9290
})
9391
.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);
9694

9795
let map = WebFeatures { features, bcd_keys };
9896
Ok(map)
9997
}
10098

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, " ");
103101
if let Ok(start) = self
104102
.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)
106104
{
107105
if start < self.bcd_keys.len() {
108106
if let Some(end) = self.bcd_keys[start + 1..]
109107
.iter()
110-
.position(|ks| !ks.bcd_key.starts_with(&suffix))
108+
.position(|ks| !ks.bcd_key_spaced.starts_with(&suffix))
111109
{
112110
return &self.bcd_keys[start + 1..start + 1 + end];
113111
}
@@ -118,154 +116,120 @@ impl WebFeatures {
118116

119117
// Compute status according to:
120118
// 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> {
122120
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
168130
.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)
170143
{
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+
});
176172
}
177-
_ => {}
178173
}
179-
Some(Baseline {
180-
support: status,
181-
asterisk: true,
182-
})
183-
} else {
184-
None
185174
}
175+
None
186176
}
187177

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> {
189179
if let Ok(i) = self
190180
.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))
192182
{
193183
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);
195185
}
196186
None
197187
}
198188

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> {
204190
if let Some(feature_data) = self.features.get(feature_name) {
205191
if feature_data.discouraged.is_some() {
206192
return None;
207193
}
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);
217195
}
218196
None
219197
}
220198
}
221199

222-
#[derive(Deserialize, Serialize, Clone, Debug)]
200+
#[derive(Deserialize, Serialize, Clone, Debug, JsonSchema)]
223201
pub struct FeatureData {
224202
/** 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)]
230204
pub spec: Vec<Url>,
231205
/** 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)]
237207
pub caniuse: Vec<String>,
238208
/** Whether a feature is considered a "baseline" web platform feature and when it achieved that status */
239209
#[serde(skip_serializing_if = "Option::is_none")]
240210
pub status: Option<SupportStatusWithByKey>,
241211
/** 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)]
247213
pub compat_features: Vec<String>,
214+
#[serde(skip_serializing)]
248215
pub description: String,
249216
pub description_html: String,
250217
#[serde(
251218
deserialize_with = "t_or_vec",
252219
default,
253220
skip_serializing_if = "Vec::is_empty"
254221
)]
222+
#[serde(skip_serializing)]
255223
pub group: Vec<String>,
256224
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)]
262226
pub snapshot: Vec<String>,
263227
/** Whether developers are formally discouraged from using this feature */
264228
#[serde(skip_serializing_if = "Option::is_none")]
265229
pub discouraged: Option<Discouraged>,
266230
}
267231

268-
#[derive(Deserialize, Serialize, Clone, Debug)]
232+
#[derive(Deserialize, Serialize, Clone, Debug, JsonSchema)]
269233
pub struct Discouraged {
270234
#[serde(default, skip_serializing_if = "Vec::is_empty")]
271235
according_to: Vec<String>,

crates/rari-doc/src/baseline.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ static WEB_FEATURES: LazyLock<Option<WebFeatures>> = LazyLock::new(|| {
3939
pub(crate) fn get_baseline<'a>(browser_compat: &[String]) -> Option<Baseline<'a>> {
4040
if let Some(ref web_features) = *WEB_FEATURES {
4141
return match &browser_compat {
42-
&[bcd_key] => web_features.feature_status(bcd_key.as_str()),
42+
&[bcd_key] => web_features.baseline_by_bcd_key(bcd_key.as_str()),
4343
_ => None,
4444
};
4545
}

0 commit comments

Comments
 (0)