mod contract;
pub mod helpers;
pub mod level_set;
pub(crate) mod macro_support;
mod macros;
pub mod plan;
mod registry;
pub mod rule;
pub mod rule_context;
pub mod rules;
mod scheduler;
#[cfg(test)]
#[allow(unused_imports)]
pub(crate) use macros::transform_examples;
#[allow(unused_imports)]
pub(crate) use macros::{alias_rule, char_targets, cmd_targets, define_rule, env_targets};
pub use contract::{ContractViolation, collect_eliminated_violations};
pub use level_set::NormalizationLevelSet;
pub use plan::{Plan, PlanBuildError, RuleAvailabilityFailure};
pub use registry::all_rules;
pub use rule::{
NormalizationLevel, PackageName, RewriteRule, RuleConsumes, RuleEffect, RuleFidelity, RuleKey,
RuleMeta, RuleProduces, RuleTarget, RuleTargetKey, RuleTargetKind,
};
pub use rule_context::{CommandView, DeclarativeView, EnvironmentView, InfixView, RuleContext};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RewriteReport {
pub rules: Vec<RewriteRuleStat>,
pub iterations: usize,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RewriteRuleStat {
pub key: RuleKey,
pub applied_count: usize,
pub skipped_count: usize,
}
impl RewriteReport {
pub(crate) fn stat_mut(&mut self, key: RuleKey) -> &mut RewriteRuleStat {
if let Some(index) = self.rules.iter().position(|entry| entry.key == key) {
return &mut self.rules[index];
}
self.rules.push(RewriteRuleStat {
key,
applied_count: 0,
skipped_count: 0,
});
self.rules
.last_mut()
.expect("newly inserted rule stat must exist")
}
pub fn mark_rule_applied(&mut self, key: RuleKey) {
self.stat_mut(key).applied_count += 1;
}
pub fn mark_rule_skipped(&mut self, key: RuleKey) {
self.stat_mut(key).skipped_count += 1;
}
pub fn record_iteration(&mut self, iterations: usize) {
self.iterations = iterations;
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RewriteError {
Rule { rule: RuleKey, kind: RuleError },
ContractViolation {
target: RuleTargetKey,
node_name: Option<String>,
},
MaxIterationsExceeded { max_iterations: usize },
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuleError {
InvalidNodeShape { message: String },
MissingMetadata { name: String },
}
impl std::fmt::Display for RewriteError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RewriteError::Rule { rule, kind } => match kind {
RuleError::InvalidNodeShape { message } => write!(f, "{rule}: {message}"),
RuleError::MissingMetadata { name } => {
write!(f, "{rule}: missing metadata for {name}")
}
},
RewriteError::ContractViolation { target, node_name } => write!(
f,
"rewrite contract violated for {} `{}` (node {:?})",
target.kind_label(),
target.name,
node_name
),
RewriteError::MaxIterationsExceeded { max_iterations } => {
write!(f, "rewrite exceeded max iterations: {max_iterations}")
}
}
}
}
impl std::error::Error for RewriteError {}
use crate::ast::Ast;
use crate::parse::ParseContext;
pub fn run(
ast: &mut Ast,
parse_ctx: &ParseContext,
plan: &Plan,
max_iterations: usize,
report: &mut RewriteReport,
) -> Result<(), RewriteError> {
scheduler::drive_fixed_point(ast, parse_ctx, plan, max_iterations, report)
}
#[cfg(test)]
pub(crate) fn run_one_rule_for_test(
ast: &mut Ast,
parse_ctx: &ParseContext,
rule: &'static dyn RewriteRule,
level: NormalizationLevel,
) -> Result<crate::TransformReport, crate::TransformError> {
let build_config = crate::BuildConfig::profile(crate::Profile::Authoring)
.rewrite_levels(NormalizationLevelSet::from(level))
.only_rule_for_tests(rule.meta().key);
let context = crate::TransformContext::from_build_config(build_config, parse_ctx)
.map_err(crate::TransformError::Build)?;
context.run_with(
ast,
parse_ctx,
&crate::TransformConfig {
rewrite_enabled: true,
lower_attributes_enabled: false,
finalize_ast: crate::FinalizeAstConfig::DISABLED,
flatten_groups: crate::FlattenGroupsConfig::DISABLED,
max_iterations: 100,
},
)
}