browserslist/queries/
mod.rs

1use crate::{
2    data::caniuse,
3    error::Error,
4    opts::Opts,
5    parser::{QueryAtom, Stats, VersionRange},
6    semver::Version,
7};
8use serde::{Deserialize, Serialize};
9use std::{borrow::Cow, fmt::Display};
10
11mod browser_accurate;
12mod browser_bounded_range;
13mod browser_unbounded_range;
14mod browserslist_config;
15mod cover;
16mod cover_by_region;
17mod current_node;
18mod dead;
19mod defaults;
20mod electron_accurate;
21mod electron_bounded_range;
22mod electron_unbounded_range;
23mod extends;
24mod firefox_esr;
25mod last_n_browsers;
26mod last_n_electron;
27mod last_n_electron_major;
28mod last_n_major_browsers;
29mod last_n_node;
30mod last_n_node_major;
31mod last_n_x_browsers;
32mod last_n_x_major_browsers;
33mod maintained_node;
34mod node_accurate;
35mod node_bounded_range;
36mod node_unbounded_range;
37mod op_mini;
38mod percentage;
39mod percentage_by_region;
40mod phantom;
41mod since;
42mod supports;
43mod unreleased_browsers;
44mod unreleased_electron;
45mod unreleased_x_browsers;
46mod years;
47
48#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
49/// Representation of browser name (or `node`) and its version.
50///
51/// When converting it to string, it will be formatted as the output of
52/// [browserslist](https://github.com/browserslist/browserslist). For example:
53///
54/// ```
55/// use browserslist::{Opts, resolve};
56///
57/// let distrib = &resolve(["firefox 93"], &Opts::default()).unwrap()[0];
58///
59/// assert_eq!(distrib.name(), "firefox");
60/// assert_eq!(distrib.version(), "93");
61/// ```
62pub struct Distrib(&'static str, Cow<'static, str>);
63
64impl Distrib {
65    #[inline]
66    fn new<S: Into<Cow<'static, str>>>(name: &'static str, version: S) -> Self {
67        Self(name, version.into())
68    }
69
70    #[inline]
71    /// Return browser name, or `node`.
72    ///
73    /// ```
74    /// use browserslist::{Opts, resolve};
75    ///
76    /// let distrib = &resolve(["firefox 93"], &Opts::default()).unwrap()[0];
77    ///
78    /// assert_eq!(distrib.name(), "firefox");
79    /// ```
80    pub fn name(&self) -> &str {
81        self.0
82    }
83
84    #[inline]
85    /// Return version string.
86    ///
87    /// ```
88    /// use browserslist::{Opts, resolve};
89    ///
90    /// let distrib = &resolve(["firefox 93"], &Opts::default()).unwrap()[0];
91    ///
92    /// assert_eq!(distrib.version(), "93");
93    /// ```
94    pub fn version(&self) -> &str {
95        &self.1
96    }
97}
98
99impl Display for Distrib {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        write!(f, "{} {}", self.0, self.1)
102    }
103}
104
105pub type QueryResult = Result<Vec<Distrib>, Error>;
106
107pub fn query(atom: QueryAtom, opts: &Opts) -> QueryResult {
108    match atom {
109        QueryAtom::Last {
110            count,
111            major,
112            name: Some(name),
113        } if name.eq_ignore_ascii_case("electron") => {
114            let count = count as usize;
115            if major {
116                last_n_electron_major::last_n_electron_major(count)
117            } else {
118                last_n_electron::last_n_electron(count)
119            }
120        }
121        QueryAtom::Last {
122            count,
123            major,
124            name: Some(name),
125        } if name.eq_ignore_ascii_case("node") => {
126            let count = count as usize;
127            if major {
128                last_n_node_major::last_n_node_major(count)
129            } else {
130                last_n_node::last_n_node(count)
131            }
132        }
133        QueryAtom::Last {
134            count,
135            major,
136            name: Some(name),
137        } => {
138            let count = count as usize;
139            if major {
140                last_n_x_major_browsers::last_n_x_major_browsers(count, name, opts)
141            } else {
142                last_n_x_browsers::last_n_x_browsers(count, name, opts)
143            }
144        }
145        QueryAtom::Last {
146            count,
147            major,
148            name: None,
149        } => {
150            let count = count as usize;
151            if major {
152                last_n_major_browsers::last_n_major_browsers(count, opts)
153            } else {
154                last_n_browsers::last_n_browsers(count, opts)
155            }
156        }
157        QueryAtom::Unreleased(Some(name)) if name.eq_ignore_ascii_case("electron") => {
158            unreleased_electron::unreleased_electron()
159        }
160        QueryAtom::Unreleased(Some(name)) => {
161            unreleased_x_browsers::unreleased_x_browsers(name, opts)
162        }
163        QueryAtom::Unreleased(None) => unreleased_browsers::unreleased_browsers(opts),
164        QueryAtom::Years(count) => years::years(count, opts),
165        QueryAtom::Since { year, month, day } => since::since(year, month, day, opts),
166        QueryAtom::Percentage {
167            comparator,
168            popularity,
169            stats: Stats::Global,
170        } => percentage::percentage(comparator, popularity),
171        QueryAtom::Percentage {
172            comparator,
173            popularity,
174            stats: Stats::Region(region),
175        } => percentage_by_region::percentage_by_region(comparator, popularity, region),
176        QueryAtom::Cover {
177            coverage,
178            stats: Stats::Global,
179        } => cover::cover(coverage),
180        QueryAtom::Cover {
181            coverage,
182            stats: Stats::Region(region),
183        } => cover_by_region::cover_by_region(coverage, region),
184        QueryAtom::Supports(name, kind) => supports::supports(name, kind, opts),
185        QueryAtom::Electron(VersionRange::Bounded(from, to)) => {
186            electron_bounded_range::electron_bounded_range(from, to)
187        }
188        QueryAtom::Electron(VersionRange::Unbounded(comparator, version)) => {
189            electron_unbounded_range::electron_unbounded_range(comparator, version)
190        }
191        QueryAtom::Electron(VersionRange::Accurate(version)) => {
192            electron_accurate::electron_accurate(version)
193        }
194        QueryAtom::Node(VersionRange::Bounded(from, to)) => {
195            node_bounded_range::node_bounded_range(from, to)
196        }
197        QueryAtom::Node(VersionRange::Unbounded(comparator, version)) => {
198            node_unbounded_range::node_unbounded_range(comparator, version)
199        }
200        QueryAtom::Node(VersionRange::Accurate(version)) => {
201            node_accurate::node_accurate(version, opts)
202        }
203        QueryAtom::Browser(name, VersionRange::Bounded(from, to)) => {
204            browser_bounded_range::browser_bounded_range(name, from, to, opts)
205        }
206        QueryAtom::Browser(name, VersionRange::Unbounded(comparator, version)) => {
207            browser_unbounded_range::browser_unbounded_range(name, comparator, version, opts)
208        }
209        QueryAtom::Browser(name, VersionRange::Accurate(version)) => {
210            browser_accurate::browser_accurate(name, version, opts)
211        }
212        QueryAtom::FirefoxESR => firefox_esr::firefox_esr(),
213        QueryAtom::OperaMini => op_mini::op_mini(),
214        QueryAtom::CurrentNode => current_node::current_node(),
215        QueryAtom::MaintainedNode => maintained_node::maintained_node(),
216        QueryAtom::Phantom(is_later_version) => phantom::phantom(is_later_version),
217        QueryAtom::BrowserslistConfig => browserslist_config::browserslist_config(opts),
218        QueryAtom::Defaults => defaults::defaults(opts),
219        QueryAtom::Dead => dead::dead(opts),
220        QueryAtom::Extends(pkg) => extends::extends(pkg, opts),
221        QueryAtom::Unknown(query) => Err(Error::UnknownQuery(query.into())),
222    }
223}
224
225pub fn count_filter_versions(name: &str, mobile_to_desktop: bool, count: usize) -> usize {
226    let jump = match name {
227        "android" => {
228            if mobile_to_desktop {
229                return count;
230            } else {
231                let last_released = &caniuse::get_browser_stat("android", mobile_to_desktop)
232                    .unwrap()
233                    .1
234                    .version_list
235                    .iter()
236                    .filter(|version| version.release_date.is_some())
237                    .map(|version| version.version)
238                    .last()
239                    .unwrap()
240                    .parse::<f32>()
241                    .unwrap();
242                (last_released - caniuse::ANDROID_EVERGREEN_FIRST) as usize
243            }
244        }
245        "op_mob" => {
246            let lastest = caniuse::get_browser_stat("android", mobile_to_desktop)
247                .unwrap()
248                .1
249                .version_list
250                .last()
251                .unwrap();
252            (lastest.version.parse::<Version>().unwrap().major() - caniuse::OP_MOB_BLINK_FIRST + 1)
253                as usize
254        }
255        _ => return count,
256    };
257    if count <= jump {
258        1
259    } else {
260        count + 1 - jump
261    }
262}