wiggle: add support for async traits; ABI func is now generic of ctx

* ctx parameter no longer accepted by wiggle::from_witx macro.
* optional async_ parameter specifies which functions are async.
* re-export async_trait::async_trait, so users don't have to take a dep.
This commit is contained in:
Pat Hickey
2021-03-04 17:26:09 -08:00
parent a5d49c07a7
commit f11cd8e7b1
9 changed files with 260 additions and 97 deletions

View File

@@ -12,22 +12,22 @@ use {
#[derive(Debug, Clone)]
pub struct Config {
pub witx: WitxConf,
pub ctx: CtxConf,
pub errors: ErrorConf,
pub async_: AsyncConf,
}
#[derive(Debug, Clone)]
pub enum ConfigField {
Witx(WitxConf),
Ctx(CtxConf),
Error(ErrorConf),
Async(AsyncConf),
}
mod kw {
syn::custom_keyword!(witx);
syn::custom_keyword!(witx_literal);
syn::custom_keyword!(ctx);
syn::custom_keyword!(errors);
syn::custom_keyword!(async_);
}
impl Parse for ConfigField {
@@ -41,14 +41,14 @@ impl Parse for ConfigField {
input.parse::<kw::witx_literal>()?;
input.parse::<Token![:]>()?;
Ok(ConfigField::Witx(WitxConf::Literal(input.parse()?)))
} else if lookahead.peek(kw::ctx) {
input.parse::<kw::ctx>()?;
input.parse::<Token![:]>()?;
Ok(ConfigField::Ctx(input.parse()?))
} else if lookahead.peek(kw::errors) {
input.parse::<kw::errors>()?;
input.parse::<Token![:]>()?;
Ok(ConfigField::Error(input.parse()?))
} else if lookahead.peek(kw::async_) {
input.parse::<kw::async_>()?;
input.parse::<Token![:]>()?;
Ok(ConfigField::Async(input.parse()?))
} else {
Err(lookahead.error())
}
@@ -58,8 +58,8 @@ impl Parse for ConfigField {
impl Config {
pub fn build(fields: impl Iterator<Item = ConfigField>, err_loc: Span) -> Result<Self> {
let mut witx = None;
let mut ctx = None;
let mut errors = None;
let mut async_ = None;
for f in fields {
match f {
ConfigField::Witx(c) => {
@@ -68,28 +68,26 @@ impl Config {
}
witx = Some(c);
}
ConfigField::Ctx(c) => {
if ctx.is_some() {
return Err(Error::new(err_loc, "duplicate `ctx` field"));
}
ctx = Some(c);
}
ConfigField::Error(c) => {
if errors.is_some() {
return Err(Error::new(err_loc, "duplicate `errors` field"));
}
errors = Some(c);
}
ConfigField::Async(c) => {
if async_.is_some() {
return Err(Error::new(err_loc, "duplicate `async` field"));
}
async_ = Some(c);
}
}
}
Ok(Config {
witx: witx
.take()
.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?,
ctx: ctx
.take()
.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?,
errors: errors.take().unwrap_or_default(),
async_: async_.take().unwrap_or_default(),
})
}
@@ -216,19 +214,6 @@ impl Parse for Literal {
}
}
#[derive(Debug, Clone)]
pub struct CtxConf {
pub name: Ident,
}
impl Parse for CtxConf {
fn parse(input: ParseStream) -> Result<Self> {
Ok(CtxConf {
name: input.parse()?,
})
}
}
#[derive(Clone, Default, Debug)]
/// Map from abi error type to rich error type
pub struct ErrorConf(HashMap<Ident, ErrorConfField>);
@@ -294,3 +279,77 @@ impl Parse for ErrorConfField {
})
}
}
#[derive(Clone, Default, Debug)]
/// Modules and funcs that should be async
pub struct AsyncConf(HashMap<String, Vec<String>>);
impl AsyncConf {
pub fn is_async(&self, module: &str, function: &str) -> bool {
self.0
.get(module)
.and_then(|fs| fs.iter().find(|f| *f == function))
.is_some()
}
}
impl Parse for AsyncConf {
fn parse(input: ParseStream) -> Result<Self> {
let content;
let _ = braced!(content in input);
let items: Punctuated<AsyncConfField, Token![,]> =
content.parse_terminated(Parse::parse)?;
let mut m: HashMap<String, Vec<String>> = HashMap::new();
use std::collections::hash_map::Entry;
for i in items {
let function_names = i
.function_names
.iter()
.map(|i| i.to_string())
.collect::<Vec<String>>();
match m.entry(i.module_name.to_string()) {
Entry::Occupied(o) => o.into_mut().extend(function_names),
Entry::Vacant(v) => {
v.insert(function_names);
}
}
}
Ok(AsyncConf(m))
}
}
#[derive(Clone)]
pub struct AsyncConfField {
pub module_name: Ident,
pub function_names: Vec<Ident>,
pub err_loc: Span,
}
impl Parse for AsyncConfField {
fn parse(input: ParseStream) -> Result<Self> {
let err_loc = input.span();
let module_name = input.parse::<Ident>()?;
let _doublecolon: Token![::] = input.parse()?;
let lookahead = input.lookahead1();
if lookahead.peek(syn::token::Brace) {
let content;
let _ = braced!(content in input);
let function_names: Punctuated<Ident, Token![,]> =
content.parse_terminated(Parse::parse)?;
Ok(AsyncConfField {
module_name,
function_names: function_names.iter().cloned().collect(),
err_loc,
})
} else if lookahead.peek(Ident) {
let name = input.parse()?;
Ok(AsyncConfField {
module_name,
function_names: vec![name],
err_loc,
})
} else {
Err(lookahead.error())
}
}
}