1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//! Browser target options.
// This file is autogenerated by build-prefixes.js. DO NOT EDIT!

use serde::{Deserialize, Serialize};

/// Browser versions to compile CSS for.
///
/// Versions are represented as a single 24-bit integer, with one byte
/// per `major.minor.patch` component.
///
/// # Example
///
/// This example represents a target of Safari 13.2.0.
///
/// ```
/// use parcel_css::targets::Browsers;
///
/// let targets = Browsers {
///   safari: Some((13 << 16) | (2 << 8)),
///   ..Browsers::default()
/// };
/// ```
#[derive(Serialize, Debug, Deserialize, Clone, Copy, Default)]
#[allow(missing_docs)]
pub struct Browsers {
  pub android: Option<u32>,
  pub chrome: Option<u32>,
  pub edge: Option<u32>,
  pub firefox: Option<u32>,
  pub ie: Option<u32>,
  pub ios_saf: Option<u32>,
  pub opera: Option<u32>,
  pub safari: Option<u32>,
  pub samsung: Option<u32>,
}

#[cfg(feature = "browserslist")]
impl Browsers {
  /// Parses a list of browserslist queries into Parcel CSS targets.
  pub fn from_browserslist<S: AsRef<str>, I: IntoIterator<Item = S>>(
    query: I,
  ) -> Result<Option<Browsers>, browserslist::Error> {
    use browserslist::{resolve, Opts};

    let res = resolve(query, &Opts::new())?;

    let mut browsers = Browsers::default();
    let mut has_any = false;
    for distrib in res {
      macro_rules! browser {
        ($browser: ident) => {{
          if let Some(v) = parse_version(distrib.version()) {
            if browsers.$browser.is_none() || v < browsers.$browser.unwrap() {
              browsers.$browser = Some(v);
              has_any = true;
            }
          }
        }};
      }

      match distrib.name() {
        "android" => browser!(android),
        "chrome" | "and_chr" => browser!(chrome),
        "edge" => browser!(edge),
        "firefox" | "and_ff" => browser!(firefox),
        "ie" => browser!(ie),
        "ios_saf" => browser!(ios_saf),
        "opera" | "op_mob" => browser!(opera),
        "safari" => browser!(safari),
        "samsung" => browser!(samsung),
        _ => {}
      }
    }

    if !has_any {
      return Ok(None);
    }

    Ok(Some(browsers))
  }
}

#[cfg(feature = "browserslist")]
fn parse_version(version: &str) -> Option<u32> {
  let version = version.split('-').next();
  if version.is_none() {
    return None;
  }

  let mut version = version.unwrap().split('.');
  let major = version.next().and_then(|v| v.parse::<u32>().ok());
  if let Some(major) = major {
    let minor = version.next().and_then(|v| v.parse::<u32>().ok()).unwrap_or(0);
    let patch = version.next().and_then(|v| v.parse::<u32>().ok()).unwrap_or(0);
    let v: u32 = (major & 0xff) << 16 | (minor & 0xff) << 8 | (patch & 0xff);
    return Some(v);
  }

  None
}