Skip to content

Commit

Permalink
feat(baseline): support new baseline
Browse files Browse the repository at this point in the history
  • Loading branch information
fiji-flo committed Aug 16, 2024
1 parent 667666d commit cd17a95
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 72 deletions.
52 changes: 43 additions & 9 deletions crates/rari-data/src/baseline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,21 @@ impl WebFeatures {
Ok(serde_json::from_str(&json_str)?)
}

pub fn feature_status(&self, features: &[&str]) -> Option<&SupportStatus> {
if features.is_empty() {
return None;
}

pub fn feature_status(&self, bcd_key: &str) -> Option<&SupportStatusWithByKey> {
self.features.values().find_map(|feature_data| {
if let Some(ref status) = feature_data.status {
if feature_data
.compat_features
.iter()
.any(|key| features.contains(&key.as_str()))
.any(|key| key == bcd_key)
{
return Some(status);
if let Some(by_key) = &status.by_compat_key {
if let Some(key_status) = by_key.get(bcd_key) {
if key_status.baseline == status.baseline {
return Some(status);
}
}
}
}
}
None
Expand All @@ -59,14 +61,29 @@ pub struct FeatureData {
pub caniuse: Vec<String>,
/** Whether a feature is considered a "baseline" web platform feature and when it achieved that status */
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<SupportStatus>,
pub status: Option<SupportStatusWithByKey>,
/** Sources of support data for this feature */
#[serde(
deserialize_with = "t_or_vec",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub compat_features: Vec<String>,
pub description: String,
pub description_html: String,
#[serde(
deserialize_with = "t_or_vec",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub group: Vec<String>,
pub name: String,
#[serde(
deserialize_with = "t_or_vec",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub snapshot: Vec<String>,
}

#[derive(Deserialize, Serialize, Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
Expand All @@ -81,7 +98,7 @@ pub enum BrowserIdentifier {
SafariIos,
}

#[derive(Deserialize, Serialize, Clone, Copy, Debug)]
#[derive(Deserialize, Serialize, Clone, Copy, Debug, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum BaselineHighLow {
High,
Expand All @@ -105,6 +122,23 @@ pub struct SupportStatus {
pub support: BTreeMap<BrowserIdentifier, String>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct SupportStatusWithByKey {
/// Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false)
#[serde(skip_serializing_if = "Option::is_none")]
pub baseline: Option<BaselineHighLow>,
/// Date the feature achieved Baseline low status
#[serde(skip_serializing_if = "Option::is_none")]
pub baseline_low_date: Option<String>,
/// Date the feature achieved Baseline high status
#[serde(skip_serializing_if = "Option::is_none")]
pub baseline_high_date: Option<String>,
/// Browser versions that most-recently introduced the feature
pub support: BTreeMap<BrowserIdentifier, String>,
#[serde(default, skip_serializing)]
pub by_compat_key: Option<BTreeMap<String, SupportStatus>>,
}

pub fn t_or_vec<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
Expand Down
4 changes: 4 additions & 0 deletions crates/rari-deps/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ pub enum DepsError {
RariIoError(#[from] rari_utils::error::RariIoError),
#[error(transparent)]
FetchError(#[from] reqwest::Error),
#[error(transparent)]
HeaderError(#[from] reqwest::header::ToStrError),
#[error("no version for webref")]
WebRefMissingVersionError,
#[error("no tarball for webref")]
WebRefMissingTarballError,
#[error("Invalid github version")]
InvalidGitHubVersion,
}
93 changes: 93 additions & 0 deletions crates/rari-deps/src/github_release.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use std::fs::{self, File};
use std::io::{BufWriter, Read, Write};
use std::path::{Path, PathBuf};

use chrono::{DateTime, Duration, Utc};
use rari_utils::io::read_to_string;
use reqwest::redirect::Policy;
use serde::{Deserialize, Serialize};

use crate::error::DepsError;

#[derive(Deserialize, Serialize, Default, Debug)]
pub struct Current {
pub latest_last_check: Option<DateTime<Utc>>,
pub version: String,
}

/// Download and unpack an npm package for a given version (defaults to latest).
pub fn get_artifact(
base_url: &str,
artifact: &str,
name: &str,
version: Option<&str>,
out_path: &Path,
) -> Result<Option<PathBuf>, DepsError> {
let version = version.unwrap_or("latest");
let package_path = out_path.join(name);
let last_check_path = package_path.join("last_check.json");
let now = Utc::now();
let current = read_to_string(last_check_path)
.ok()
.and_then(|current| serde_json::from_str::<Current>(&current).ok())
.unwrap_or_default();
if version != current.version
|| version == "latest"
&& current.latest_last_check.unwrap_or_default() < now - Duration::days(1)
{
let prev_url = format!(
"{base_url}/releases/download/{}/{artifact}",
current.version
);
let url = if version == "latest" {
let client = reqwest::blocking::ClientBuilder::default()
.redirect(Policy::none())
.build()?;
let res = client
.get(format!("{base_url}/releases/latest/download/{artifact}"))
.send()?;
res.headers()
.get("location")
.ok_or(DepsError::InvalidGitHubVersion)?
.to_str()?
.to_string()
} else {
format!("{base_url}/releases/download/{version}/{artifact}")
};

let artifact_path = package_path.join(artifact);
let download_update = if artifact_path.exists() {
prev_url != url
} else {
true
};

if download_update {
if package_path.exists() {
fs::remove_dir_all(&package_path)?;
}
fs::create_dir_all(&package_path)?;
let mut buf = vec![];
let _ = reqwest::blocking::get(url)?.read_to_end(&mut buf)?;

let file = File::create(artifact_path).unwrap();
let mut buffed = BufWriter::new(file);

buffed.write_all(&buf)?;
}

if version == "latest" {
fs::write(
package_path.join("last_check.json"),
serde_json::to_string_pretty(&Current {
version: version.to_string(),
latest_last_check: Some(now),
})?,
)?;
}
if download_update {
return Ok(Some(package_path));
}
}
Ok(None)
}
1 change: 1 addition & 0 deletions crates/rari-deps/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod bcd;
pub mod error;
pub mod external_json;
pub mod github_release;
pub mod mdn_data;
pub mod npm;
pub mod web_ext_examples;
Expand Down
11 changes: 9 additions & 2 deletions crates/rari-deps/src/web_features.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
use std::path::Path;

use crate::error::DepsError;
use crate::npm::get_package;
use crate::github_release::get_artifact;

pub fn update_web_features(base_path: &Path) -> Result<(), DepsError> {
get_package("web-features", None, base_path)?;
//get_package("web-features", None, base_path)?;
get_artifact(
"https://github.com/web-platform-dx/web-features",
"data.extended.json",
"baseline",
None,
base_path,
)?;
Ok(())
}
66 changes: 8 additions & 58 deletions crates/rari-doc/src/baseline.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
use std::sync::LazyLock;

use rari_data::baseline::{SupportStatus, WebFeatures};
use rari_data::baseline::{SupportStatusWithByKey, WebFeatures};
use rari_types::globals::data_dir;
use tracing::warn;

static WEB_FEATURES: LazyLock<Option<WebFeatures>> = LazyLock::new(|| {
let web_features = WebFeatures::from_file(
&data_dir()
.join("web-features")
.join("package")
.join("data.json"),
);
let web_features =
WebFeatures::from_file(&data_dir().join("baseline").join("data.extended.json"));
match web_features {
Ok(web_features) => Some(web_features),
Err(e) => {
Expand All @@ -20,58 +16,12 @@ static WEB_FEATURES: LazyLock<Option<WebFeatures>> = LazyLock::new(|| {
}
});

static DISALLOW_LIST: &[&str] = &[
// https://github.com/web-platform-dx/web-features/blob/cf718ad/feature-group-definitions/async-clipboard.yml
"api.Clipboard.read",
"api.Clipboard.readText",
"api.Clipboard.write",
"api.Clipboard.writeText",
"api.ClipboardEvent",
"api.ClipboardEvent.ClipboardEvent",
"api.ClipboardEvent.clipboardData",
"api.ClipboardItem",
"api.ClipboardItem.ClipboardItem",
"api.ClipboardItem.getType",
"api.ClipboardItem.presentationStyle",
"api.ClipboardItem.types",
"api.Navigator.clipboard",
"api.Permissions.permission_clipboard-read",
// https://github.com/web-platform-dx/web-features/blob/cf718ad/feature-group-definitions/custom-elements.yml
"api.CustomElementRegistry",
"api.CustomElementRegistry.builtin_element_support",
"api.CustomElementRegistry.define",
"api.Window.customElements",
"css.selectors.defined",
"css.selectors.host",
"css.selectors.host-context",
"css.selectors.part",
// https://github.com/web-platform-dx/web-features/blob/cf718ad/feature-group-definitions/input-event.yml
"api.Element.input_event",
"api.InputEvent.InputEvent",
"api.InputEvent.data",
"api.InputEvent.dataTransfer",
"api.InputEvent.getTargetRanges",
"api.InputEvent.inputType",
// https://github.com/web-platform-dx/web-features/issues/1038
// https://github.com/web-platform-dx/web-features/blob/64d2cfd/features/screen-orientation-lock.dist.yml
"api.ScreenOrientation.lock",
"api.ScreenOrientation.unlock",
];

pub fn get_baseline(browser_compat: &[String]) -> Option<&'static SupportStatus> {
pub fn get_baseline(browser_compat: &[String]) -> Option<&'static SupportStatusWithByKey> {
if let Some(ref web_features) = *WEB_FEATURES {
if browser_compat.is_empty() {
return None;
}
let filtered_browser_compat = browser_compat.iter().filter_map(
|query|
// temporary blocklist while we wait for per-key baseline statuses
// or another solution to the baseline/bcd table discrepancy problem
if !DISALLOW_LIST.contains(&query.as_str()) {
Some(query.as_str())
} else {None}
).collect::<Vec<&str>>();
return web_features.feature_status(&filtered_browser_compat);
return match &browser_compat {
&[bcd_key] => web_features.feature_status(bcd_key.as_str()),
_ => None,
};
}
None
}
4 changes: 2 additions & 2 deletions crates/rari-doc/src/docs/json.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::PathBuf;

use chrono::NaiveDateTime;
use rari_data::baseline::SupportStatus;
use rari_data::baseline::SupportStatusWithByKey;
use rari_types::locale::{Locale, Native};
use serde::Serialize;

Expand Down Expand Up @@ -111,7 +111,7 @@ pub struct JsonDoc {
pub title: String,
pub toc: Vec<TocEntry>,
#[serde(skip_serializing_if = "Option::is_none")]
pub baseline: Option<&'static SupportStatus>,
pub baseline: Option<&'static SupportStatusWithByKey>,
#[serde(rename = "browserCompat", skip_serializing_if = "Vec::is_empty")]
pub browser_compat: Vec<String>,
}
Expand Down
2 changes: 1 addition & 1 deletion crates/rari-doc/src/docs/title.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub fn root_doc_url(url: &str) -> Option<&str> {
if url[m[1]..].starts_with("/blog") || url[m[1]..].starts_with("/curriculum") {
return None;
}
if url[m[1]..].starts_with("/docs/Web") {
if url[m[1]..].starts_with("/docs/Web/") {
return Some(&url[..*m.get(4).unwrap_or(&url.len())]);
}
Some(&url[..*m.get(3).unwrap_or(&url.len())])
Expand Down

0 comments on commit cd17a95

Please sign in to comment.