//! Parsing of CfgFlags as command line arguments, as in //! //! rustc main.rs --cfg foo --cfg 'feature="bar"' use std::{fmt, str::FromStr}; use cfg::{CfgDiff, CfgOptions}; use intern::Symbol; use rustc_hash::FxHashMap; use serde::Serialize; #[derive(Clone, Eq, PartialEq, Debug, Serialize)] pub enum CfgFlag { Atom(String), KeyValue { key: String, value: String }, } impl FromStr for CfgFlag { type Err = String; fn from_str(s: &str) -> Result { let res = match s.split_once('=') { Some((key, value)) => { if !(value.starts_with('"') && value.ends_with('"')) { return Err(format!("Invalid cfg ({s:?}), value should be in quotes")); } let key = key.to_owned(); let value = value[1..value.len() - 1].to_string(); CfgFlag::KeyValue { key, value } } None => CfgFlag::Atom(s.into()), }; Ok(res) } } impl<'de> serde::Deserialize<'de> for CfgFlag { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom) } } impl Extend for CfgOptions { fn extend>(&mut self, iter: T) { for cfg_flag in iter { match cfg_flag { CfgFlag::Atom(it) => self.insert_atom(Symbol::intern(&it)), CfgFlag::KeyValue { key, value } => { self.insert_key_value(Symbol::intern(&key), Symbol::intern(&value)) } } } } } impl FromIterator for CfgOptions { fn from_iter>(iter: T) -> Self { let mut this = CfgOptions::default(); this.extend(iter); this } } impl fmt::Display for CfgFlag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { CfgFlag::Atom(atom) => f.write_str(atom), CfgFlag::KeyValue { key, value } => { f.write_str(key)?; f.write_str("=")?; f.write_str(value) } } } } /// A set of cfg-overrides per crate. #[derive(Default, Debug, Clone, Eq, PartialEq)] pub struct CfgOverrides { /// A global set of overrides matching all crates. pub global: CfgDiff, /// A set of overrides matching specific crates. pub selective: FxHashMap, } impl CfgOverrides { pub fn len(&self) -> usize { self.global.len() + self.selective.values().map(|it| it.len()).sum::() } pub fn apply(&self, cfg_options: &mut CfgOptions, name: &str) { if !self.global.is_empty() { cfg_options.apply_diff(self.global.clone()); }; if let Some(diff) = self.selective.get(name) { cfg_options.apply_diff(diff.clone()); }; } }