1use serde::{Deserialize, Serialize};
5
6#[derive(Serialize, Debug, Deserialize, Clone, Copy, Default)]
24#[allow(missing_docs)]
25pub struct Browsers {
26 pub android: Option<u32>,
27 pub chrome: Option<u32>,
28 pub edge: Option<u32>,
29 pub firefox: Option<u32>,
30 pub ie: Option<u32>,
31 pub ios_saf: Option<u32>,
32 pub opera: Option<u32>,
33 pub safari: Option<u32>,
34 pub samsung: Option<u32>,
35}
36
37#[cfg(feature = "browserslist")]
38impl Browsers {
39 pub fn from_browserslist<S: AsRef<str>, I: IntoIterator<Item = S>>(
41 query: I,
42 ) -> Result<Option<Browsers>, browserslist::Error> {
43 use browserslist::{resolve, Opts};
44
45 let res = resolve(query, &Opts::new())?;
46
47 let mut browsers = Browsers::default();
48 let mut has_any = false;
49 for distrib in res {
50 macro_rules! browser {
51 ($browser: ident) => {{
52 if let Some(v) = parse_version(distrib.version()) {
53 if browsers.$browser.is_none() || v < browsers.$browser.unwrap() {
54 browsers.$browser = Some(v);
55 has_any = true;
56 }
57 }
58 }};
59 }
60
61 match distrib.name() {
62 "android" => browser!(android),
63 "chrome" | "and_chr" => browser!(chrome),
64 "edge" => browser!(edge),
65 "firefox" | "and_ff" => browser!(firefox),
66 "ie" => browser!(ie),
67 "ios_saf" => browser!(ios_saf),
68 "opera" | "op_mob" => browser!(opera),
69 "safari" => browser!(safari),
70 "samsung" => browser!(samsung),
71 _ => {}
72 }
73 }
74
75 if !has_any {
76 return Ok(None);
77 }
78
79 Ok(Some(browsers))
80 }
81}
82
83#[cfg(feature = "browserslist")]
84fn parse_version(version: &str) -> Option<u32> {
85 let version = version.split('-').next();
86 if version.is_none() {
87 return None;
88 }
89
90 let mut version = version.unwrap().split('.');
91 let major = version.next().and_then(|v| v.parse::<u32>().ok());
92 if let Some(major) = major {
93 let minor = version.next().and_then(|v| v.parse::<u32>().ok()).unwrap_or(0);
94 let patch = version.next().and_then(|v| v.parse::<u32>().ok()).unwrap_or(0);
95 let v: u32 = (major & 0xff) << 16 | (minor & 0xff) << 8 | (patch & 0xff);
96 return Some(v);
97 }
98
99 None
100}