use wiggle_generate::config::AsyncFunctions;
use {
proc_macro2::Span,
std::collections::HashMap,
syn::{
braced,
parse::{Parse, ParseStream},
punctuated::Punctuated,
Error, Ident, Path, Result, Token,
},
wiggle_generate::config::WitxConf,
};
#[derive(Debug, Clone)]
pub struct Config {
pub target: TargetConf,
pub witx: WitxConf,
pub ctx: CtxConf,
pub modules: ModulesConf,
pub async_: AsyncConf,
}
#[derive(Debug, Clone)]
pub enum ConfigField {
Target(TargetConf),
Witx(WitxConf),
Ctx(CtxConf),
Modules(ModulesConf),
Async(AsyncConf),
}
mod kw {
syn::custom_keyword!(target);
syn::custom_keyword!(witx);
syn::custom_keyword!(witx_literal);
syn::custom_keyword!(ctx);
syn::custom_keyword!(modules);
syn::custom_keyword!(name);
syn::custom_keyword!(docs);
syn::custom_keyword!(function_override);
syn::custom_keyword!(block_on);
}
impl Parse for ConfigField {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::target) {
input.parse::<kw::target>()?;
input.parse::<Token![:]>()?;
Ok(ConfigField::Target(input.parse()?))
} else if lookahead.peek(kw::witx) {
input.parse::<kw::witx>()?;
input.parse::<Token![:]>()?;
Ok(ConfigField::Witx(WitxConf::Paths(input.parse()?)))
} else if lookahead.peek(kw::witx_literal) {
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::modules) {
input.parse::<kw::modules>()?;
input.parse::<Token![:]>()?;
Ok(ConfigField::Modules(input.parse()?))
} else if lookahead.peek(Token![async]) {
input.parse::<Token![async]>()?;
input.parse::<Token![:]>()?;
Ok(ConfigField::Async(AsyncConf {
blocking: false,
functions: input.parse()?,
}))
} else if lookahead.peek(kw::block_on) {
input.parse::<kw::block_on>()?;
input.parse::<Token![:]>()?;
Ok(ConfigField::Async(AsyncConf {
blocking: true,
functions: input.parse()?,
}))
} else {
Err(lookahead.error())
}
}
}
impl Config {
pub fn build(fields: impl Iterator<Item = ConfigField>, err_loc: Span) -> Result<Self> {
let mut target = None;
let mut witx = None;
let mut ctx = None;
let mut modules = None;
let mut async_ = None;
for f in fields {
match f {
ConfigField::Target(c) => {
if target.is_some() {
return Err(Error::new(err_loc, "duplicate `target` field"));
}
target = Some(c);
}
ConfigField::Witx(c) => {
if witx.is_some() {
return Err(Error::new(err_loc, "duplicate `witx` field"));
}
witx = Some(c);
}
ConfigField::Ctx(c) => {
if ctx.is_some() {
return Err(Error::new(err_loc, "duplicate `ctx` field"));
}
ctx = Some(c);
}
ConfigField::Modules(c) => {
if modules.is_some() {
return Err(Error::new(err_loc, "duplicate `modules` field"));
}
modules = Some(c);
}
ConfigField::Async(c) => {
if async_.is_some() {
return Err(Error::new(err_loc, "duplicate `async` field"));
}
async_ = Some(c);
}
}
}
Ok(Config {
target: target.ok_or_else(|| Error::new(err_loc, "`target` field required"))?,
witx: witx.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?,
ctx: ctx.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?,
modules: modules.ok_or_else(|| Error::new(err_loc, "`modules` field required"))?,
async_: async_.unwrap_or_default(),
})
}
pub fn load_document(&self) -> witx::Document {
self.witx.load_document()
}
}
impl Parse for Config {
fn parse(input: ParseStream) -> Result<Self> {
let contents;
let _lbrace = braced!(contents in input);
let fields: Punctuated<ConfigField, Token![,]> =
contents.parse_terminated(ConfigField::parse)?;
Ok(Config::build(fields.into_iter(), input.span())?)
}
}
#[derive(Debug, Clone)]
pub struct CtxConf {
pub name: syn::Type,
}
impl Parse for CtxConf {
fn parse(input: ParseStream) -> Result<Self> {
Ok(CtxConf {
name: input.parse()?,
})
}
}
#[derive(Debug, Clone)]
pub struct TargetConf {
pub path: Path,
}
impl Parse for TargetConf {
fn parse(input: ParseStream) -> Result<Self> {
Ok(TargetConf {
path: input.parse()?,
})
}
}
enum ModuleConfField {
Name(Ident),
Docs(String),
}
impl Parse for ModuleConfField {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::name) {
input.parse::<kw::name>()?;
input.parse::<Token![:]>()?;
Ok(ModuleConfField::Name(input.parse()?))
} else if lookahead.peek(kw::docs) {
input.parse::<kw::docs>()?;
input.parse::<Token![:]>()?;
let docs: syn::LitStr = input.parse()?;
Ok(ModuleConfField::Docs(docs.value()))
} else {
Err(lookahead.error())
}
}
}
#[derive(Debug, Clone)]
pub struct ModuleConf {
pub name: Ident,
pub docs: Option<String>,
}
impl ModuleConf {
fn build(fields: impl Iterator<Item = ModuleConfField>, err_loc: Span) -> Result<Self> {
let mut name = None;
let mut docs = None;
for f in fields {
match f {
ModuleConfField::Name(c) => {
if name.is_some() {
return Err(Error::new(err_loc, "duplicate `name` field"));
}
name = Some(c);
}
ModuleConfField::Docs(c) => {
if docs.is_some() {
return Err(Error::new(err_loc, "duplicate `docs` field"));
}
docs = Some(c);
}
}
}
Ok(ModuleConf {
name: name.ok_or_else(|| Error::new(err_loc, "`name` field required"))?,
docs,
})
}
}
impl Parse for ModuleConf {
fn parse(input: ParseStream) -> Result<Self> {
let contents;
let _lbrace = braced!(contents in input);
let fields: Punctuated<ModuleConfField, Token![,]> =
contents.parse_terminated(ModuleConfField::parse)?;
Ok(ModuleConf::build(fields.into_iter(), input.span())?)
}
}
#[derive(Debug, Clone)]
pub struct ModulesConf {
pub mods: HashMap<String, ModuleConf>,
}
impl ModulesConf {
pub fn iter(&self) -> impl Iterator<Item = (&String, &ModuleConf)> {
self.mods.iter()
}
}
impl Parse for ModulesConf {
fn parse(input: ParseStream) -> Result<Self> {
let contents;
let _lbrace = braced!(contents in input);
let fields: Punctuated<(String, ModuleConf), Token![,]> =
contents.parse_terminated(|i| {
let name = i.parse::<Ident>()?.to_string();
i.parse::<Token![=>]>()?;
let val = i.parse()?;
Ok((name, val))
})?;
Ok(ModulesConf {
mods: fields.into_iter().collect(),
})
}
}
#[derive(Clone, Default, Debug)]
pub struct AsyncConf {
blocking: bool,
functions: AsyncFunctions,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Asyncness {
Sync,
Blocking,
Async,
}
impl Asyncness {
pub fn is_sync(&self) -> bool {
match self {
Asyncness::Sync => true,
_ => false,
}
}
}
impl AsyncConf {
pub fn is_async(&self, module: &str, function: &str) -> Asyncness {
let a = if self.blocking {
Asyncness::Blocking
} else {
Asyncness::Async
};
match &self.functions {
AsyncFunctions::Some(fs) => {
if fs
.get(module)
.and_then(|fs| fs.iter().find(|f| *f == function))
.is_some()
{
a
} else {
Asyncness::Sync
}
}
AsyncFunctions::All => a,
}
}
}