browserslist/queries/
mod.rs

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