use crate::{LookupItem, lookup_keys};
use anyhow::Result;
use wit_parser::{Function, FunctionKind, Resolve, WorldKey};
bitflags::bitflags! {
#[derive(Default, Copy, Clone, Debug)]
pub struct FunctionFlags: u8 {
const ASYNC = 1 << 0;
const TRAPPABLE = 1 << 1;
const STORE = 1 << 2;
const TRACING = 1 << 3;
const VERBOSE_TRACING = 1 << 4;
const IGNORE_WIT = 1 << 5;
const EXACT = 1 << 6;
}
}
#[derive(Default, Debug, Clone)]
pub struct FunctionConfig {
rules: Vec<FunctionRule>,
pub(crate) default: FunctionFlags,
}
#[derive(Debug, Clone)]
struct FunctionRule {
filter: String,
flags: FunctionFlags,
used: bool,
}
#[derive(Debug, Clone)]
pub enum FunctionFilter {
Name(String),
Default,
}
impl FunctionConfig {
pub fn new() -> FunctionConfig {
FunctionConfig::default()
}
pub fn push(&mut self, filter: FunctionFilter, flags: FunctionFlags) {
match filter {
FunctionFilter::Name(filter) => {
self.rules.push(FunctionRule {
filter,
flags,
used: false,
});
}
FunctionFilter::Default => {
self.default = flags;
}
}
}
pub(crate) fn flags(
&mut self,
resolve: &Resolve,
ns: Option<&WorldKey>,
func: &Function,
) -> FunctionFlags {
let mut wit_flags = FunctionFlags::empty();
match &func.kind {
FunctionKind::Freestanding
| FunctionKind::Method(_)
| FunctionKind::Static(_)
| FunctionKind::Constructor(_) => {}
FunctionKind::AsyncFreestanding
| FunctionKind::AsyncMethod(_)
| FunctionKind::AsyncStatic(_) => {
wit_flags |= FunctionFlags::ASYNC | FunctionFlags::STORE;
}
}
let mut ret = FunctionFlags::empty();
self.add_function_flags(resolve, ns, &func.name, &mut ret);
if !ret.contains(FunctionFlags::IGNORE_WIT) {
ret |= wit_flags;
}
ret
}
pub(crate) fn resource_drop_flags(
&mut self,
resolve: &Resolve,
ns: Option<&WorldKey>,
resource_name: &str,
) -> FunctionFlags {
let mut ret = FunctionFlags::empty();
self.add_function_flags(resolve, ns, &format!("[drop]{resource_name}"), &mut ret);
ret
}
fn add_function_flags(
&mut self,
resolve: &Resolve,
key: Option<&WorldKey>,
name: &str,
base: &mut FunctionFlags,
) {
let mut apply_rules = |name: &str, is_exact: bool| {
for rule in self.rules.iter_mut() {
if name != rule.filter {
continue;
}
if !is_exact && rule.flags.contains(FunctionFlags::EXACT) {
continue;
}
rule.used = true;
*base |= rule.flags;
return true;
}
false
};
match key {
Some(key) => {
for (lookup, projection) in lookup_keys(resolve, key, LookupItem::Name(name)) {
if apply_rules(&lookup, projection.is_empty()) {
return;
}
}
}
None => {
if apply_rules(name, true) {
return;
}
}
}
*base |= self.default;
}
pub(crate) fn assert_all_rules_used(&self, kind: &str) -> Result<()> {
let mut unused = Vec::new();
for rule in self.rules.iter().filter(|r| !r.used) {
unused.push(format!("{:?}: {:?}", rule.filter, rule.flags));
}
if unused.is_empty() {
return Ok(());
}
anyhow::bail!("unused `{kind}` rules found: {unused:?}");
}
}