use anyhow::{Result, bail};
use std::collections::HashSet;
use std::fmt;
use wit_parser::{Function, FunctionKind, Resolve, WorldKey};
#[cfg_attr(feature = "clap", derive(clap::Parser))]
#[derive(Clone, Default, Debug)]
pub struct AsyncFilterSet {
#[cfg_attr(
feature = "clap",
arg(
long = "async",
value_parser = parse_async,
value_delimiter =',',
value_name = "FILTER",
),
)]
async_: Vec<Async>,
#[cfg_attr(feature = "clap", arg(skip))]
used_options: HashSet<usize>,
}
#[cfg(feature = "clap")]
fn parse_async(s: &str) -> Result<Async, String> {
Ok(Async::parse(s))
}
impl AsyncFilterSet {
pub fn all(async_: bool) -> AsyncFilterSet {
AsyncFilterSet {
async_: vec![Async {
enabled: async_,
filter: AsyncFilter::All,
}],
used_options: HashSet::new(),
}
}
pub fn is_async(
&mut self,
resolve: &Resolve,
interface: Option<&WorldKey>,
func: &Function,
is_import: bool,
) -> bool {
let name_to_test = match interface {
Some(key) => format!("{}#{}", resolve.name_world_key(key), func.name),
None => func.name.clone(),
};
for (i, opt) in self.async_.iter().enumerate() {
let name = match &opt.filter {
AsyncFilter::All => {
self.used_options.insert(i);
return opt.enabled;
}
AsyncFilter::Function(s) => s,
AsyncFilter::Import(s) => {
if !is_import {
continue;
}
s
}
AsyncFilter::Export(s) => {
if is_import {
continue;
}
s
}
};
if *name == name_to_test {
self.used_options.insert(i);
return opt.enabled;
}
}
match &func.kind {
FunctionKind::Freestanding
| FunctionKind::Method(_)
| FunctionKind::Static(_)
| FunctionKind::Constructor(_) => false,
FunctionKind::AsyncFreestanding
| FunctionKind::AsyncMethod(_)
| FunctionKind::AsyncStatic(_) => true,
}
}
pub fn debug_opts(&self) -> impl Iterator<Item = String> + '_ {
self.async_.iter().map(|opt| opt.to_string())
}
pub fn ensure_all_used(&self) -> Result<()> {
for (i, opt) in self.async_.iter().enumerate() {
if self.used_options.contains(&i) {
continue;
}
if !matches!(opt.filter, AsyncFilter::All) {
bail!("unused async option: {opt}");
}
}
Ok(())
}
pub fn any_enabled(&self) -> bool {
self.async_.iter().any(|o| o.enabled)
}
pub fn push(&mut self, directive: &str) {
self.async_.push(Async::parse(directive));
}
}
#[derive(Debug, Clone)]
struct Async {
enabled: bool,
filter: AsyncFilter,
}
impl Async {
fn parse(s: &str) -> Async {
let (s, enabled) = match s.strip_prefix('-') {
Some(s) => (s, false),
None => (s, true),
};
let filter = match s {
"all" => AsyncFilter::All,
other => match other.strip_prefix("import:") {
Some(s) => AsyncFilter::Import(s.to_string()),
None => match other.strip_prefix("export:") {
Some(s) => AsyncFilter::Export(s.to_string()),
None => AsyncFilter::Function(s.to_string()),
},
},
};
Async { enabled, filter }
}
}
impl fmt::Display for Async {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.enabled {
write!(f, "-")?;
}
self.filter.fmt(f)
}
}
#[derive(Debug, Clone)]
enum AsyncFilter {
All,
Function(String),
Import(String),
Export(String),
}
impl fmt::Display for AsyncFilter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AsyncFilter::All => write!(f, "all"),
AsyncFilter::Function(s) => write!(f, "{s}"),
AsyncFilter::Import(s) => write!(f, "import:{s}"),
AsyncFilter::Export(s) => write!(f, "export:{s}"),
}
}
}