cargo-platform-0.1.0/Cargo.toml.orig010066400017500001750000000006441354273114100155440ustar0000000000000000[package] name = "cargo-platform" version = "0.1.0" authors = ["The Cargo Project Developers"] edition = "2018" license = "MIT OR Apache-2.0" homepage = "https://github.com/rust-lang/cargo" repository = "https://github.com/rust-lang/cargo" documentation = "https://docs.rs/cargo-platform" description = "Cargo's representation of a target platform." [dependencies] serde = { version = "1.0.82", features = ['derive'] } cargo-platform-0.1.0/Cargo.toml0000644000000016510000000000000120070ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "cargo-platform" version = "0.1.0" authors = ["The Cargo Project Developers"] description = "Cargo's representation of a target platform." homepage = "https://github.com/rust-lang/cargo" documentation = "https://docs.rs/cargo-platform" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/cargo" [dependencies.serde] version = "1.0.82" features = ["derive"] cargo-platform-0.1.0/examples/matches.rs010066400017500001750000000027721354273114100164710ustar0000000000000000//! This example demonstrates how to filter a Platform based on the current //! host target. use cargo_platform::{Cfg, Platform}; use std::process::Command; use std::str::FromStr; static EXAMPLES: &[&str] = &[ "cfg(windows)", "cfg(unix)", "cfg(target_os=\"macos\")", "cfg(target_os=\"linux\")", "cfg(any(target_arch=\"x86\", target_arch=\"x86_64\"))", ]; fn main() { let target = get_target(); let cfgs = get_cfgs(); println!("host target={} cfgs:", target); for cfg in &cfgs { println!(" {}", cfg); } let mut examples: Vec<&str> = EXAMPLES.iter().copied().collect(); examples.push(target.as_str()); for example in examples { let p = Platform::from_str(example).unwrap(); println!("{:?} matches: {:?}", example, p.matches(&target, &cfgs)); } } fn get_target() -> String { let output = Command::new("rustc") .arg("-Vv") .output() .expect("rustc failed to run"); let stdout = String::from_utf8(output.stdout).unwrap(); for line in stdout.lines() { if line.starts_with("host: ") { return String::from(&line[6..]); } } panic!("Failed to find host: {}", stdout); } fn get_cfgs() -> Vec { let output = Command::new("rustc") .arg("--print=cfg") .output() .expect("rustc failed to run"); let stdout = String::from_utf8(output.stdout).unwrap(); stdout .lines() .map(|line| Cfg::from_str(line).unwrap()) .collect() } cargo-platform-0.1.0/src/cfg.rs010066400017500001750000000230351354273114100145500ustar0000000000000000use crate::error::{ParseError, ParseErrorKind::*}; use std::fmt; use std::iter; use std::str::{self, FromStr}; /// A cfg expression. #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)] pub enum CfgExpr { Not(Box), All(Vec), Any(Vec), Value(Cfg), } /// A cfg value. #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)] pub enum Cfg { /// A named cfg value, like `unix`. Name(String), /// A key/value cfg pair, like `target_os = "linux"`. KeyPair(String, String), } #[derive(PartialEq)] enum Token<'a> { LeftParen, RightParen, Ident(&'a str), Comma, Equals, String(&'a str), } #[derive(Clone)] struct Tokenizer<'a> { s: iter::Peekable>, orig: &'a str, } struct Parser<'a> { t: Tokenizer<'a>, } impl FromStr for Cfg { type Err = ParseError; fn from_str(s: &str) -> Result { let mut p = Parser::new(s); let e = p.cfg()?; if let Some(rest) = p.rest() { return Err(ParseError::new( p.t.orig, UnterminatedExpression(rest.to_string()), )); } Ok(e) } } impl fmt::Display for Cfg { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Cfg::Name(ref s) => s.fmt(f), Cfg::KeyPair(ref k, ref v) => write!(f, "{} = \"{}\"", k, v), } } } impl CfgExpr { /// Utility function to check if the key, "cfg(..)" matches the `target_cfg` pub fn matches_key(key: &str, target_cfg: &[Cfg]) -> bool { if key.starts_with("cfg(") && key.ends_with(')') { let cfg = &key[4..key.len() - 1]; CfgExpr::from_str(cfg) .ok() .map(|ce| ce.matches(target_cfg)) .unwrap_or(false) } else { false } } pub fn matches(&self, cfg: &[Cfg]) -> bool { match *self { CfgExpr::Not(ref e) => !e.matches(cfg), CfgExpr::All(ref e) => e.iter().all(|e| e.matches(cfg)), CfgExpr::Any(ref e) => e.iter().any(|e| e.matches(cfg)), CfgExpr::Value(ref e) => cfg.contains(e), } } } impl FromStr for CfgExpr { type Err = ParseError; fn from_str(s: &str) -> Result { let mut p = Parser::new(s); let e = p.expr()?; if let Some(rest) = p.rest() { return Err(ParseError::new( p.t.orig, UnterminatedExpression(rest.to_string()), )); } Ok(e) } } impl fmt::Display for CfgExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { CfgExpr::Not(ref e) => write!(f, "not({})", e), CfgExpr::All(ref e) => write!(f, "all({})", CommaSep(e)), CfgExpr::Any(ref e) => write!(f, "any({})", CommaSep(e)), CfgExpr::Value(ref e) => write!(f, "{}", e), } } } struct CommaSep<'a, T>(&'a [T]); impl<'a, T: fmt::Display> fmt::Display for CommaSep<'a, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for (i, v) in self.0.iter().enumerate() { if i > 0 { write!(f, ", ")?; } write!(f, "{}", v)?; } Ok(()) } } impl<'a> Parser<'a> { fn new(s: &'a str) -> Parser<'a> { Parser { t: Tokenizer { s: s.char_indices().peekable(), orig: s, }, } } fn expr(&mut self) -> Result { match self.peek() { Some(Ok(Token::Ident(op @ "all"))) | Some(Ok(Token::Ident(op @ "any"))) => { self.t.next(); let mut e = Vec::new(); self.eat(&Token::LeftParen)?; while !self.r#try(&Token::RightParen) { e.push(self.expr()?); if !self.r#try(&Token::Comma) { self.eat(&Token::RightParen)?; break; } } if op == "all" { Ok(CfgExpr::All(e)) } else { Ok(CfgExpr::Any(e)) } } Some(Ok(Token::Ident("not"))) => { self.t.next(); self.eat(&Token::LeftParen)?; let e = self.expr()?; self.eat(&Token::RightParen)?; Ok(CfgExpr::Not(Box::new(e))) } Some(Ok(..)) => self.cfg().map(CfgExpr::Value), Some(Err(..)) => Err(self.t.next().unwrap().err().unwrap()), None => Err(ParseError::new( self.t.orig, IncompleteExpr("start of a cfg expression"), )), } } fn cfg(&mut self) -> Result { match self.t.next() { Some(Ok(Token::Ident(name))) => { let e = if self.r#try(&Token::Equals) { let val = match self.t.next() { Some(Ok(Token::String(s))) => s, Some(Ok(t)) => { return Err(ParseError::new( self.t.orig, UnexpectedToken { expected: "a string", found: t.classify(), }, )) } Some(Err(e)) => return Err(e), None => { return Err(ParseError::new(self.t.orig, IncompleteExpr("a string"))) } }; Cfg::KeyPair(name.to_string(), val.to_string()) } else { Cfg::Name(name.to_string()) }; Ok(e) } Some(Ok(t)) => Err(ParseError::new( self.t.orig, UnexpectedToken { expected: "identifier", found: t.classify(), }, )), Some(Err(e)) => Err(e), None => Err(ParseError::new(self.t.orig, IncompleteExpr("identifier"))), } } fn peek(&mut self) -> Option, ParseError>> { self.t.clone().next() } fn r#try(&mut self, token: &Token<'a>) -> bool { match self.peek() { Some(Ok(ref t)) if token == t => {} _ => return false, } self.t.next(); true } fn eat(&mut self, token: &Token<'a>) -> Result<(), ParseError> { match self.t.next() { Some(Ok(ref t)) if token == t => Ok(()), Some(Ok(t)) => Err(ParseError::new( self.t.orig, UnexpectedToken { expected: token.classify(), found: t.classify(), }, )), Some(Err(e)) => Err(e), None => Err(ParseError::new( self.t.orig, IncompleteExpr(token.classify()), )), } } /// Returns the rest of the input from the current location. fn rest(&self) -> Option<&str> { let mut s = self.t.s.clone(); loop { match s.next() { Some((_, ' ')) => {} Some((start, _ch)) => return Some(&self.t.orig[start..]), None => return None, } } } } impl<'a> Iterator for Tokenizer<'a> { type Item = Result, ParseError>; fn next(&mut self) -> Option, ParseError>> { loop { match self.s.next() { Some((_, ' ')) => {} Some((_, '(')) => return Some(Ok(Token::LeftParen)), Some((_, ')')) => return Some(Ok(Token::RightParen)), Some((_, ',')) => return Some(Ok(Token::Comma)), Some((_, '=')) => return Some(Ok(Token::Equals)), Some((start, '"')) => { while let Some((end, ch)) = self.s.next() { if ch == '"' { return Some(Ok(Token::String(&self.orig[start + 1..end]))); } } return Some(Err(ParseError::new(self.orig, UnterminatedString))); } Some((start, ch)) if is_ident_start(ch) => { while let Some(&(end, ch)) = self.s.peek() { if !is_ident_rest(ch) { return Some(Ok(Token::Ident(&self.orig[start..end]))); } else { self.s.next(); } } return Some(Ok(Token::Ident(&self.orig[start..]))); } Some((_, ch)) => { return Some(Err(ParseError::new(self.orig, UnexpectedChar(ch)))); } None => return None, } } } } fn is_ident_start(ch: char) -> bool { ch == '_' || ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') } fn is_ident_rest(ch: char) -> bool { is_ident_start(ch) || ('0' <= ch && ch <= '9') } impl<'a> Token<'a> { fn classify(&self) -> &'static str { match *self { Token::LeftParen => "`(`", Token::RightParen => "`)`", Token::Ident(..) => "an identifier", Token::Comma => "`,`", Token::Equals => "`=`", Token::String(..) => "a string", } } } cargo-platform-0.1.0/src/error.rs010066400017500001750000000035271354273114100151460ustar0000000000000000use std::fmt; #[derive(Debug)] pub struct ParseError { kind: ParseErrorKind, orig: String, } #[derive(Debug)] pub enum ParseErrorKind { UnterminatedString, UnexpectedChar(char), UnexpectedToken { expected: &'static str, found: &'static str, }, IncompleteExpr(&'static str), UnterminatedExpression(String), InvalidTarget(String), #[doc(hidden)] __Nonexhaustive, } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "failed to parse `{}` as a cfg expression: {}", self.orig, self.kind ) } } impl fmt::Display for ParseErrorKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use ParseErrorKind::*; match self { UnterminatedString => write!(f, "unterminated string in cfg"), UnexpectedChar(ch) => write!( f, "unexpected character `{}` in cfg, expected parens, a comma, \ an identifier, or a string", ch ), UnexpectedToken { expected, found } => { write!(f, "expected {}, found {}", expected, found) } IncompleteExpr(expected) => { write!(f, "expected {}, but cfg expression ended", expected) } UnterminatedExpression(s) => { write!(f, "unexpected content `{}` found after cfg expression", s) } InvalidTarget(s) => write!(f, "invalid target specifier: {}", s), __Nonexhaustive => unreachable!(), } } } impl std::error::Error for ParseError {} impl ParseError { pub fn new(orig: &str, kind: ParseErrorKind) -> ParseError { ParseError { kind, orig: orig.to_string(), } } } cargo-platform-0.1.0/src/lib.rs010066400017500001750000000060601354273114100145560ustar0000000000000000//! Platform definition used by Cargo. //! //! This defines a [`Platform`] type which is used in Cargo to specify a target platform. //! There are two kinds, a named target like `x86_64-apple-darwin`, and a "cfg expression" //! like `cfg(any(target_os = "macos", target_os = "ios"))`. //! //! See `examples/matches.rs` for an example of how to match against a `Platform`. //! //! [`Platform`]: enum.Platform.html use std::fmt; use std::str::FromStr; mod cfg; mod error; pub use cfg::{Cfg, CfgExpr}; pub use error::{ParseError, ParseErrorKind}; /// Platform definition. #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)] pub enum Platform { /// A named platform, like `x86_64-apple-darwin`. Name(String), /// A cfg expression, like `cfg(windows)`. Cfg(CfgExpr), } impl Platform { /// Returns whether the Platform matches the given target and cfg. /// /// The named target and cfg values should be obtained from `rustc`. pub fn matches(&self, name: &str, cfg: &[Cfg]) -> bool { match *self { Platform::Name(ref p) => p == name, Platform::Cfg(ref p) => p.matches(cfg), } } fn validate_named_platform(name: &str) -> Result<(), ParseError> { if let Some(ch) = name .chars() .find(|&c| !(c.is_alphanumeric() || c == '_' || c == '-' || c == '.')) { if name.chars().any(|c| c == '(') { return Err(ParseError::new( name, ParseErrorKind::InvalidTarget( "unexpected `(` character, cfg expressions must start with `cfg(`" .to_string(), ), )); } return Err(ParseError::new( name, ParseErrorKind::InvalidTarget(format!( "unexpected character {} in target name", ch )), )); } Ok(()) } } impl serde::Serialize for Platform { fn serialize(&self, s: S) -> Result where S: serde::Serializer, { self.to_string().serialize(s) } } impl<'de> serde::Deserialize<'de> for Platform { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let s = String::deserialize(deserializer)?; FromStr::from_str(&s).map_err(serde::de::Error::custom) } } impl FromStr for Platform { type Err = ParseError; fn from_str(s: &str) -> Result { if s.starts_with("cfg(") && s.ends_with(')') { let s = &s[4..s.len() - 1]; s.parse().map(Platform::Cfg) } else { Platform::validate_named_platform(s)?; Ok(Platform::Name(s.to_string())) } } } impl fmt::Display for Platform { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Platform::Name(ref n) => n.fmt(f), Platform::Cfg(ref e) => write!(f, "cfg({})", e), } } } cargo-platform-0.1.0/tests/test_cfg.rs010066400017500001750000000127011354273114100161600ustar0000000000000000use cargo_platform::{Cfg, CfgExpr, Platform}; use std::fmt; use std::str::FromStr; macro_rules! c { ($a:ident) => { Cfg::Name(stringify!($a).to_string()) }; ($a:ident = $e:expr) => { Cfg::KeyPair(stringify!($a).to_string(), $e.to_string()) }; } macro_rules! e { (any($($t:tt),*)) => (CfgExpr::Any(vec![$(e!($t)),*])); (all($($t:tt),*)) => (CfgExpr::All(vec![$(e!($t)),*])); (not($($t:tt)*)) => (CfgExpr::Not(Box::new(e!($($t)*)))); (($($t:tt)*)) => (e!($($t)*)); ($($t:tt)*) => (CfgExpr::Value(c!($($t)*))); } fn good(s: &str, expected: T) where T: FromStr + PartialEq + fmt::Debug, T::Err: fmt::Display, { let c = match T::from_str(s) { Ok(c) => c, Err(e) => panic!("failed to parse `{}`: {}", s, e), }; assert_eq!(c, expected); } fn bad(s: &str, err: &str) where T: FromStr + fmt::Display, T::Err: fmt::Display, { let e = match T::from_str(s) { Ok(cfg) => panic!("expected `{}` to not parse but got {}", s, cfg), Err(e) => e.to_string(), }; assert!( e.contains(err), "when parsing `{}`,\n\"{}\" not contained \ inside: {}", s, err, e ); } #[test] fn cfg_syntax() { good("foo", c!(foo)); good("_bar", c!(_bar)); good(" foo", c!(foo)); good(" foo ", c!(foo)); good(" foo = \"bar\"", c!(foo = "bar")); good("foo=\"\"", c!(foo = "")); good(" foo=\"3\" ", c!(foo = "3")); good("foo = \"3 e\"", c!(foo = "3 e")); } #[test] fn cfg_syntax_bad() { bad::("", "but cfg expression ended"); bad::(" ", "but cfg expression ended"); bad::("\t", "unexpected character"); bad::("7", "unexpected character"); bad::("=", "expected identifier"); bad::(",", "expected identifier"); bad::("(", "expected identifier"); bad::("foo (", "unexpected content `(` found after cfg expression"); bad::("bar =", "expected a string"); bad::("bar = \"", "unterminated string"); bad::( "foo, bar", "unexpected content `, bar` found after cfg expression", ); } #[test] fn cfg_expr() { good("foo", e!(foo)); good("_bar", e!(_bar)); good(" foo", e!(foo)); good(" foo ", e!(foo)); good(" foo = \"bar\"", e!(foo = "bar")); good("foo=\"\"", e!(foo = "")); good(" foo=\"3\" ", e!(foo = "3")); good("foo = \"3 e\"", e!(foo = "3 e")); good("all()", e!(all())); good("all(a)", e!(all(a))); good("all(a, b)", e!(all(a, b))); good("all(a, )", e!(all(a))); good("not(a = \"b\")", e!(not(a = "b"))); good("not(all(a))", e!(not(all(a)))); } #[test] fn cfg_expr_bad() { bad::(" ", "but cfg expression ended"); bad::(" all", "expected `(`"); bad::("all(a", "expected `)`"); bad::("not", "expected `(`"); bad::("not(a", "expected `)`"); bad::("a = ", "expected a string"); bad::("all(not())", "expected identifier"); bad::( "foo(a)", "unexpected content `(a)` found after cfg expression", ); } #[test] fn cfg_matches() { assert!(e!(foo).matches(&[c!(bar), c!(foo), c!(baz)])); assert!(e!(any(foo)).matches(&[c!(bar), c!(foo), c!(baz)])); assert!(e!(any(foo, bar)).matches(&[c!(bar)])); assert!(e!(any(foo, bar)).matches(&[c!(foo)])); assert!(e!(all(foo, bar)).matches(&[c!(foo), c!(bar)])); assert!(e!(all(foo, bar)).matches(&[c!(foo), c!(bar)])); assert!(e!(not(foo)).matches(&[c!(bar)])); assert!(e!(not(foo)).matches(&[])); assert!(e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(bar)])); assert!(e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(foo), c!(bar)])); assert!(!e!(foo).matches(&[])); assert!(!e!(foo).matches(&[c!(bar)])); assert!(!e!(foo).matches(&[c!(fo)])); assert!(!e!(any(foo)).matches(&[])); assert!(!e!(any(foo)).matches(&[c!(bar)])); assert!(!e!(any(foo)).matches(&[c!(bar), c!(baz)])); assert!(!e!(all(foo)).matches(&[c!(bar), c!(baz)])); assert!(!e!(all(foo, bar)).matches(&[c!(bar)])); assert!(!e!(all(foo, bar)).matches(&[c!(foo)])); assert!(!e!(all(foo, bar)).matches(&[])); assert!(!e!(not(bar)).matches(&[c!(bar)])); assert!(!e!(not(bar)).matches(&[c!(baz), c!(bar)])); assert!(!e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(foo)])); } #[test] fn bad_target_name() { bad::( "any(cfg(unix), cfg(windows))", "failed to parse `any(cfg(unix), cfg(windows))` as a cfg expression: \ invalid target specifier: unexpected `(` character, \ cfg expressions must start with `cfg(`", ); bad::( "!foo", "failed to parse `!foo` as a cfg expression: \ invalid target specifier: unexpected character ! in target name", ); } #[test] fn round_trip_platform() { fn rt(s: &str) { let p = Platform::from_str(s).unwrap(); let s2 = p.to_string(); let p2 = Platform::from_str(&s2).unwrap(); assert_eq!(p, p2); } rt("x86_64-apple-darwin"); rt("foo"); rt("cfg(windows)"); rt("cfg(target_os = \"windows\")"); rt( "cfg(any(all(any(target_os = \"android\", target_os = \"linux\"), \ any(target_arch = \"aarch64\", target_arch = \"arm\", target_arch = \"powerpc64\", \ target_arch = \"x86\", target_arch = \"x86_64\")), \ all(target_os = \"freebsd\", target_arch = \"x86_64\")))", ); } cargo-platform-0.1.0/.cargo_vcs_info.json0000644000000001120000000000000140000ustar00{ "git": { "sha1": "3cd05e28950f0fe4e1d14bc14162c7e2661af397" } } cargo-platform-0.1.0/Cargo.lock0000644000000051020000000000000117570ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "cargo-platform" version = "0.1.0" dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro2" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" "checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"