preset_env_base/
lib.rs

1//! Base crate for `preset-env`-like crates.
2//!
3//! This crate provides an interface to convert `browserslist` query to
4//! something usable.
5
6use anyhow::Error;
7use serde::{Deserialize, Serialize};
8use st_map::StaticMap;
9
10use self::version::Version;
11
12pub mod query;
13pub mod version;
14
15/// A map without allocation.
16#[derive(Debug, Default, Deserialize, Clone, Copy, Serialize, StaticMap, PartialEq, Eq)]
17#[serde(deny_unknown_fields)]
18pub struct BrowserData<T: Default> {
19    #[serde(default)]
20    pub chrome: T,
21    #[serde(default, rename = "chrome-android")]
22    pub chrome_android: T,
23    #[serde(default, rename = "firefox-android")]
24    pub firerfox_android: T,
25    #[serde(default, rename = "opera-android")]
26    pub opera_android: T,
27
28    #[serde(default)]
29    pub quest: T,
30    #[serde(default, rename = "react-native")]
31    pub react_native: T,
32
33    #[serde(default)]
34    pub and_chr: T,
35    #[serde(default)]
36    pub and_ff: T,
37    #[serde(default)]
38    pub op_mob: T,
39    #[serde(default)]
40    pub ie: T,
41    #[serde(default)]
42    pub edge: T,
43    #[serde(default)]
44    pub firefox: T,
45    #[serde(default)]
46    pub safari: T,
47    #[serde(default)]
48    pub node: T,
49    #[serde(default)]
50    pub ios: T,
51    #[serde(default)]
52    pub samsung: T,
53    #[serde(default)]
54    pub opera: T,
55    #[serde(default)]
56    pub android: T,
57    #[serde(default)]
58    pub electron: T,
59    #[serde(default)]
60    pub phantom: T,
61    #[serde(default)]
62    pub opera_mobile: T,
63    #[serde(default)]
64    pub rhino: T,
65    #[serde(default)]
66    pub deno: T,
67    #[serde(default)]
68    pub hermes: T,
69    #[serde(default)]
70    pub oculus: T,
71    #[serde(default)]
72    pub bun: T,
73}
74
75impl<T> BrowserData<T>
76where
77    T: Default,
78{
79    pub fn insert(&mut self, k: &str, v: T) -> T {
80        for (key, value) in self.iter_mut() {
81            if k == key {
82                return std::mem::replace(value, v);
83            }
84        }
85
86        panic!("unknown key: {}", k)
87    }
88}
89
90/// A map of browser names to data for feature support in browser.
91///
92/// This type mainly stores `minimum version for each browsers with support for
93/// a feature`.
94pub type Versions = BrowserData<Option<Version>>;
95
96impl BrowserData<Option<Version>> {
97    /// Returns true if all fields are [None].
98    pub fn is_any_target(&self) -> bool {
99        self.iter().all(|(_, v)| v.is_none())
100    }
101
102    /// Parses the value returned from `browserslist` as [Versions].
103    pub fn parse_versions(distribs: Vec<browserslist::Distrib>) -> Result<Self, Error> {
104        fn remap(key: &str) -> &str {
105            match key {
106                "and_chr" => "chrome",
107                "and_ff" => "firefox",
108                "ie_mob" => "ie",
109                "ios_saf" => "ios",
110                "op_mob" => "opera",
111                _ => key,
112            }
113        }
114
115        let mut data: Versions = BrowserData::default();
116        for dist in distribs {
117            let browser = dist.name();
118            let browser = remap(browser);
119            let version = dist.version();
120            match browser {
121                "and_qq" | "and_uc" | "baidu" | "bb" | "kaios" | "op_mini" => continue,
122
123                _ => {}
124            }
125
126            let version = version
127                .split_once('-')
128                .map(|(version, _)| version)
129                .unwrap_or(version)
130                .parse()
131                .ok();
132
133            let version = match version {
134                Some(v) => v,
135                None => continue,
136            };
137
138            // lowest version
139            if data[&browser].map(|v| v > version).unwrap_or(true) {
140                for (k, v) in data.iter_mut() {
141                    if browser == k {
142                        *v = Some(version);
143                    }
144                }
145            }
146        }
147
148        Ok(data)
149    }
150}