diff options
Diffstat (limited to 'gen/src/cfg.rs')
-rw-r--r-- | gen/src/cfg.rs | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/gen/src/cfg.rs b/gen/src/cfg.rs new file mode 100644 index 00000000..da589085 --- /dev/null +++ b/gen/src/cfg.rs @@ -0,0 +1,133 @@ +use crate::gen::{CfgEvaluator, CfgResult}; +use crate::syntax::cfg::CfgExpr; +use crate::syntax::report::Errors; +use crate::syntax::Api; +use quote::quote; +use std::collections::BTreeSet as Set; +use syn::Error; + +pub(super) struct UnsupportedCfgEvaluator; + +impl CfgEvaluator for UnsupportedCfgEvaluator { + fn eval(&self, name: &str, value: Option<&str>) -> CfgResult { + let _ = name; + let _ = value; + let msg = "cfg attribute is not supported".to_owned(); + CfgResult::Undetermined { msg } + } +} + +pub(super) fn strip( + cx: &mut Errors, + cfg_errors: &mut Set<String>, + cfg_evaluator: &dyn CfgEvaluator, + apis: &mut Vec<Api>, +) { + apis.retain(|api| eval(cx, cfg_errors, cfg_evaluator, api.cfg())); + for api in apis { + match api { + Api::Struct(strct) => strct + .fields + .retain(|field| eval(cx, cfg_errors, cfg_evaluator, &field.cfg)), + Api::Enum(enm) => enm + .variants + .retain(|variant| eval(cx, cfg_errors, cfg_evaluator, &variant.cfg)), + _ => {} + } + } +} + +pub(super) fn eval( + cx: &mut Errors, + cfg_errors: &mut Set<String>, + cfg_evaluator: &dyn CfgEvaluator, + expr: &CfgExpr, +) -> bool { + match try_eval(cfg_evaluator, expr) { + Ok(value) => value, + Err(errors) => { + for error in errors { + if cfg_errors.insert(error.to_string()) { + cx.push(error); + } + } + false + } + } +} + +fn try_eval(cfg_evaluator: &dyn CfgEvaluator, expr: &CfgExpr) -> Result<bool, Vec<Error>> { + match expr { + CfgExpr::Unconditional => Ok(true), + CfgExpr::Eq(ident, string) => { + let key = ident.to_string(); + let value = string.as_ref().map(|string| string.value()); + match cfg_evaluator.eval(&key, value.as_deref()) { + CfgResult::True => Ok(true), + CfgResult::False => Ok(false), + CfgResult::Undetermined { msg } => { + let span = quote!(#ident #string); + Err(vec![Error::new_spanned(span, msg)]) + } + } + } + CfgExpr::All(list) => { + let mut all_errors = Vec::new(); + for subexpr in list { + match try_eval(cfg_evaluator, subexpr) { + Ok(true) => {} + Ok(false) => return Ok(false), + Err(errors) => all_errors.extend(errors), + } + } + if all_errors.is_empty() { + Ok(true) + } else { + Err(all_errors) + } + } + CfgExpr::Any(list) => { + let mut all_errors = Vec::new(); + for subexpr in list { + match try_eval(cfg_evaluator, subexpr) { + Ok(true) => return Ok(true), + Ok(false) => {} + Err(errors) => all_errors.extend(errors), + } + } + if all_errors.is_empty() { + Ok(false) + } else { + Err(all_errors) + } + } + CfgExpr::Not(subexpr) => match try_eval(cfg_evaluator, subexpr) { + Ok(value) => Ok(!value), + Err(errors) => Err(errors), + }, + } +} + +impl Api { + fn cfg(&self) -> &CfgExpr { + match self { + Api::Include(include) => &include.cfg, + Api::Struct(strct) => &strct.cfg, + Api::Enum(enm) => &enm.cfg, + Api::CxxType(ety) | Api::RustType(ety) => &ety.cfg, + Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.cfg, + Api::TypeAlias(alias) => &alias.cfg, + Api::Impl(imp) => &imp.cfg, + } + } +} + +impl From<bool> for CfgResult { + fn from(value: bool) -> Self { + if value { + CfgResult::True + } else { + CfgResult::False + } + } +} |