Path: blob/main/crates/wiggle/generate/src/config.rs
1693 views
use {1proc_macro2::{Span, TokenStream},2std::{collections::HashMap, path::PathBuf},3syn::{4Error, Ident, LitStr, Result, Token, braced, bracketed,5parse::{Parse, ParseStream},6punctuated::Punctuated,7},8};910#[derive(Debug, Clone)]11pub struct Config {12pub witx: WitxConf,13pub errors: ErrorConf,14pub async_: AsyncConf,15pub wasmtime: bool,16pub tracing: TracingConf,17pub mutable: bool,18}1920mod kw {21syn::custom_keyword!(witx);22syn::custom_keyword!(witx_literal);23syn::custom_keyword!(block_on);24syn::custom_keyword!(errors);25syn::custom_keyword!(target);26syn::custom_keyword!(wasmtime);27syn::custom_keyword!(mutable);28syn::custom_keyword!(tracing);29syn::custom_keyword!(disable_for);30syn::custom_keyword!(trappable);31}3233#[derive(Debug, Clone)]34pub enum ConfigField {35Witx(WitxConf),36Error(ErrorConf),37Async(AsyncConf),38Wasmtime(bool),39Tracing(TracingConf),40Mutable(bool),41}4243impl Parse for ConfigField {44fn parse(input: ParseStream) -> Result<Self> {45let lookahead = input.lookahead1();46if lookahead.peek(kw::witx) {47input.parse::<kw::witx>()?;48input.parse::<Token![:]>()?;49Ok(ConfigField::Witx(WitxConf::Paths(input.parse()?)))50} else if lookahead.peek(kw::witx_literal) {51input.parse::<kw::witx_literal>()?;52input.parse::<Token![:]>()?;53Ok(ConfigField::Witx(WitxConf::Literal(input.parse()?)))54} else if lookahead.peek(kw::errors) {55input.parse::<kw::errors>()?;56input.parse::<Token![:]>()?;57Ok(ConfigField::Error(input.parse()?))58} else if lookahead.peek(Token![async]) {59input.parse::<Token![async]>()?;60input.parse::<Token![:]>()?;61Ok(ConfigField::Async(AsyncConf {62block_with: None,63functions: input.parse()?,64}))65} else if lookahead.peek(kw::block_on) {66input.parse::<kw::block_on>()?;67let block_with = if input.peek(syn::token::Bracket) {68let content;69let _ = bracketed!(content in input);70content.parse()?71} else {72quote::quote!(wiggle::run_in_dummy_executor)73};74input.parse::<Token![:]>()?;75Ok(ConfigField::Async(AsyncConf {76block_with: Some(block_with),77functions: input.parse()?,78}))79} else if lookahead.peek(kw::wasmtime) {80input.parse::<kw::wasmtime>()?;81input.parse::<Token![:]>()?;82Ok(ConfigField::Wasmtime(input.parse::<syn::LitBool>()?.value))83} else if lookahead.peek(kw::tracing) {84input.parse::<kw::tracing>()?;85input.parse::<Token![:]>()?;86Ok(ConfigField::Tracing(input.parse()?))87} else if lookahead.peek(kw::mutable) {88input.parse::<kw::mutable>()?;89input.parse::<Token![:]>()?;90Ok(ConfigField::Mutable(input.parse::<syn::LitBool>()?.value))91} else {92Err(lookahead.error())93}94}95}9697impl Config {98pub fn build(fields: impl Iterator<Item = ConfigField>, err_loc: Span) -> Result<Self> {99let mut witx = None;100let mut errors = None;101let mut async_ = None;102let mut wasmtime = None;103let mut tracing = None;104let mut mutable = None;105for f in fields {106match f {107ConfigField::Witx(c) => {108if witx.is_some() {109return Err(Error::new(err_loc, "duplicate `witx` field"));110}111witx = Some(c);112}113ConfigField::Error(c) => {114if errors.is_some() {115return Err(Error::new(err_loc, "duplicate `errors` field"));116}117errors = Some(c);118}119ConfigField::Async(c) => {120if async_.is_some() {121return Err(Error::new(err_loc, "duplicate `async` field"));122}123async_ = Some(c);124}125ConfigField::Wasmtime(c) => {126if wasmtime.is_some() {127return Err(Error::new(err_loc, "duplicate `wasmtime` field"));128}129wasmtime = Some(c);130}131ConfigField::Tracing(c) => {132if tracing.is_some() {133return Err(Error::new(err_loc, "duplicate `tracing` field"));134}135tracing = Some(c);136}137ConfigField::Mutable(c) => {138if mutable.is_some() {139return Err(Error::new(err_loc, "duplicate `mutable` field"));140}141mutable = Some(c);142}143}144}145Ok(Config {146witx: witx147.take()148.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?,149errors: errors.take().unwrap_or_default(),150async_: async_.take().unwrap_or_default(),151wasmtime: wasmtime.unwrap_or(true),152tracing: tracing.unwrap_or_default(),153mutable: mutable.unwrap_or(true),154})155}156157/// Load the `witx` document for the configuration.158///159/// # Panics160///161/// This method will panic if the paths given in the `witx` field were not valid documents.162pub fn load_document(&self) -> witx::Document {163self.witx.load_document()164}165}166167impl Parse for Config {168fn parse(input: ParseStream) -> Result<Self> {169let contents;170let _lbrace = braced!(contents in input);171let fields: Punctuated<ConfigField, Token![,]> =172contents.parse_terminated(ConfigField::parse, Token![,])?;173Ok(Config::build(fields.into_iter(), input.span())?)174}175}176177/// The witx document(s) that will be loaded from a [`Config`](struct.Config.html).178///179/// A witx interface definition can be provided either as a collection of relative paths to180/// documents, or as a single inlined string literal. Note that `(use ...)` directives are not181/// permitted when providing a string literal.182#[derive(Debug, Clone)]183pub enum WitxConf {184/// A collection of paths pointing to witx files.185Paths(Paths),186/// A single witx document, provided as a string literal.187Literal(Literal),188}189190impl WitxConf {191/// Load the `witx` document.192///193/// # Panics194///195/// This method will panic if the paths given in the `witx` field were not valid documents, or196/// if any of the given documents were not syntactically valid.197pub fn load_document(&self) -> witx::Document {198match self {199Self::Paths(paths) => witx::load(paths.as_ref()).expect("loading witx"),200Self::Literal(doc) => witx::parse(doc.as_ref()).expect("parsing witx"),201}202}203}204205/// A collection of paths, pointing to witx documents.206#[derive(Debug, Clone)]207pub struct Paths(Vec<PathBuf>);208209impl Paths {210/// Create a new, empty collection of paths.211pub fn new() -> Self {212Default::default()213}214}215216impl Default for Paths {217fn default() -> Self {218Self(Default::default())219}220}221222impl AsRef<[PathBuf]> for Paths {223fn as_ref(&self) -> &[PathBuf] {224self.0.as_ref()225}226}227228impl AsMut<[PathBuf]> for Paths {229fn as_mut(&mut self) -> &mut [PathBuf] {230self.0.as_mut()231}232}233234impl FromIterator<PathBuf> for Paths {235fn from_iter<I>(iter: I) -> Self236where237I: IntoIterator<Item = PathBuf>,238{239Self(iter.into_iter().collect())240}241}242243impl Parse for Paths {244fn parse(input: ParseStream) -> Result<Self> {245let content;246let _ = bracketed!(content in input);247let path_lits: Punctuated<LitStr, Token![,]> =248content.parse_terminated(Parse::parse, Token![,])?;249250let expanded_paths = path_lits251.iter()252.map(|lit| {253PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join(lit.value())254})255.collect::<Vec<PathBuf>>();256257Ok(Paths(expanded_paths))258}259}260261/// A single witx document, provided as a string literal.262#[derive(Debug, Clone)]263pub struct Literal(String);264265impl AsRef<str> for Literal {266fn as_ref(&self) -> &str {267self.0.as_ref()268}269}270271impl Parse for Literal {272fn parse(input: ParseStream) -> Result<Self> {273Ok(Self(input.parse::<syn::LitStr>()?.value()))274}275}276277#[derive(Clone, Default, Debug)]278/// Map from abi error type to rich error type279pub struct ErrorConf(HashMap<Ident, ErrorConfField>);280281impl ErrorConf {282pub fn iter(&self) -> impl Iterator<Item = (&Ident, &ErrorConfField)> {283self.0.iter()284}285}286287impl Parse for ErrorConf {288fn parse(input: ParseStream) -> Result<Self> {289let content;290let _ = braced!(content in input);291let items: Punctuated<ErrorConfField, Token![,]> =292content.parse_terminated(Parse::parse, Token![,])?;293let mut m = HashMap::new();294for i in items {295match m.insert(i.abi_error().clone(), i.clone()) {296None => {}297Some(prev_def) => {298return Err(Error::new(299*i.err_loc(),300format!(301"duplicate definition of rich error type for {:?}: previously defined at {:?}",302i.abi_error(),303prev_def.err_loc(),304),305));306}307}308}309Ok(ErrorConf(m))310}311}312313#[derive(Debug, Clone)]314pub enum ErrorConfField {315Trappable(TrappableErrorConfField),316User(UserErrorConfField),317}318impl ErrorConfField {319pub fn abi_error(&self) -> &Ident {320match self {321Self::Trappable(t) => &t.abi_error,322Self::User(u) => &u.abi_error,323}324}325pub fn err_loc(&self) -> &Span {326match self {327Self::Trappable(t) => &t.err_loc,328Self::User(u) => &u.err_loc,329}330}331}332333impl Parse for ErrorConfField {334fn parse(input: ParseStream) -> Result<Self> {335let err_loc = input.span();336let abi_error = input.parse::<Ident>()?;337let _arrow: Token![=>] = input.parse()?;338339let lookahead = input.lookahead1();340if lookahead.peek(kw::trappable) {341let _ = input.parse::<kw::trappable>()?;342let rich_error = input.parse()?;343Ok(ErrorConfField::Trappable(TrappableErrorConfField {344abi_error,345rich_error,346err_loc,347}))348} else {349let rich_error = input.parse::<syn::Path>()?;350Ok(ErrorConfField::User(UserErrorConfField {351abi_error,352rich_error,353err_loc,354}))355}356}357}358359#[derive(Clone, Debug)]360pub struct TrappableErrorConfField {361pub abi_error: Ident,362pub rich_error: Ident,363pub err_loc: Span,364}365366#[derive(Clone)]367pub struct UserErrorConfField {368pub abi_error: Ident,369pub rich_error: syn::Path,370pub err_loc: Span,371}372373impl std::fmt::Debug for UserErrorConfField {374fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {375f.debug_struct("ErrorConfField")376.field("abi_error", &self.abi_error)377.field("rich_error", &"(...)")378.field("err_loc", &self.err_loc)379.finish()380}381}382383#[derive(Clone, Default, Debug)]384/// Modules and funcs that have async signatures385pub struct AsyncConf {386block_with: Option<TokenStream>,387functions: AsyncFunctions,388}389390#[derive(Clone, Debug)]391pub enum Asyncness {392/// Wiggle function is synchronous, wasmtime Func is synchronous393Sync,394/// Wiggle function is asynchronous, but wasmtime Func is synchronous395Blocking { block_with: TokenStream },396/// Wiggle function and wasmtime Func are asynchronous.397Async,398}399400impl Asyncness {401pub fn is_async(&self) -> bool {402match self {403Self::Async => true,404_ => false,405}406}407pub fn blocking(&self) -> Option<&TokenStream> {408match self {409Self::Blocking { block_with } => Some(block_with),410_ => None,411}412}413pub fn is_sync(&self) -> bool {414match self {415Self::Sync => true,416_ => false,417}418}419}420421#[derive(Clone, Debug)]422pub enum AsyncFunctions {423Some(HashMap<String, Vec<String>>),424All,425}426impl Default for AsyncFunctions {427fn default() -> Self {428AsyncFunctions::Some(HashMap::default())429}430}431432impl AsyncConf {433pub fn get(&self, module: &str, function: &str) -> Asyncness {434let a = match &self.block_with {435Some(block_with) => Asyncness::Blocking {436block_with: block_with.clone(),437},438None => Asyncness::Async,439};440match &self.functions {441AsyncFunctions::Some(fs) => {442if fs443.get(module)444.and_then(|fs| fs.iter().find(|f| *f == function))445.is_some()446{447a448} else {449Asyncness::Sync450}451}452AsyncFunctions::All => a,453}454}455456pub fn contains_async(&self, module: &witx::Module) -> bool {457for f in module.funcs() {458if self.get(module.name.as_str(), f.name.as_str()).is_async() {459return true;460}461}462false463}464}465466impl Parse for AsyncFunctions {467fn parse(input: ParseStream) -> Result<Self> {468let content;469let lookahead = input.lookahead1();470if lookahead.peek(syn::token::Brace) {471let _ = braced!(content in input);472let items: Punctuated<FunctionField, Token![,]> =473content.parse_terminated(Parse::parse, Token![,])?;474let mut functions: HashMap<String, Vec<String>> = HashMap::new();475use std::collections::hash_map::Entry;476for i in items {477let function_names = i478.function_names479.iter()480.map(|i| i.to_string())481.collect::<Vec<String>>();482match functions.entry(i.module_name.to_string()) {483Entry::Occupied(o) => o.into_mut().extend(function_names),484Entry::Vacant(v) => {485v.insert(function_names);486}487}488}489Ok(AsyncFunctions::Some(functions))490} else if lookahead.peek(Token![*]) {491let _: Token![*] = input.parse().unwrap();492Ok(AsyncFunctions::All)493} else {494Err(lookahead.error())495}496}497}498499#[derive(Clone)]500pub struct FunctionField {501pub module_name: Ident,502pub function_names: Vec<Ident>,503pub err_loc: Span,504}505506impl Parse for FunctionField {507fn parse(input: ParseStream) -> Result<Self> {508let err_loc = input.span();509let module_name = input.parse::<Ident>()?;510let _doublecolon: Token![::] = input.parse()?;511let lookahead = input.lookahead1();512if lookahead.peek(syn::token::Brace) {513let content;514let _ = braced!(content in input);515let function_names: Punctuated<Ident, Token![,]> =516content.parse_terminated(Parse::parse, Token![,])?;517Ok(FunctionField {518module_name,519function_names: function_names.iter().cloned().collect(),520err_loc,521})522} else if lookahead.peek(Ident) {523let name = input.parse()?;524Ok(FunctionField {525module_name,526function_names: vec![name],527err_loc,528})529} else {530Err(lookahead.error())531}532}533}534535#[derive(Clone)]536pub struct WasmtimeConfig {537pub c: Config,538pub target: syn::Path,539}540541#[derive(Clone)]542pub enum WasmtimeConfigField {543Core(ConfigField),544Target(syn::Path),545}546impl WasmtimeConfig {547pub fn build(fields: impl Iterator<Item = WasmtimeConfigField>, err_loc: Span) -> Result<Self> {548let mut target = None;549let mut cs = Vec::new();550for f in fields {551match f {552WasmtimeConfigField::Target(c) => {553if target.is_some() {554return Err(Error::new(err_loc, "duplicate `target` field"));555}556target = Some(c);557}558WasmtimeConfigField::Core(c) => cs.push(c),559}560}561let c = Config::build(cs.into_iter(), err_loc)?;562Ok(WasmtimeConfig {563c,564target: target565.take()566.ok_or_else(|| Error::new(err_loc, "`target` field required"))?,567})568}569}570571impl Parse for WasmtimeConfig {572fn parse(input: ParseStream) -> Result<Self> {573let contents;574let _lbrace = braced!(contents in input);575let fields: Punctuated<WasmtimeConfigField, Token![,]> =576contents.parse_terminated(WasmtimeConfigField::parse, Token![,])?;577Ok(WasmtimeConfig::build(fields.into_iter(), input.span())?)578}579}580581impl Parse for WasmtimeConfigField {582fn parse(input: ParseStream) -> Result<Self> {583if input.peek(kw::target) {584input.parse::<kw::target>()?;585input.parse::<Token![:]>()?;586Ok(WasmtimeConfigField::Target(input.parse()?))587} else {588Ok(WasmtimeConfigField::Core(input.parse()?))589}590}591}592593#[derive(Clone, Debug)]594pub struct TracingConf {595enabled: bool,596excluded_functions: HashMap<String, Vec<String>>,597}598599impl TracingConf {600pub fn enabled_for(&self, module: &str, function: &str) -> bool {601if !self.enabled {602return false;603}604self.excluded_functions605.get(module)606.and_then(|fs| fs.iter().find(|f| *f == function))607.is_none()608}609}610611impl Default for TracingConf {612fn default() -> Self {613Self {614enabled: true,615excluded_functions: HashMap::new(),616}617}618}619620impl Parse for TracingConf {621fn parse(input: ParseStream) -> Result<Self> {622let enabled = input.parse::<syn::LitBool>()?.value;623624let lookahead = input.lookahead1();625if lookahead.peek(kw::disable_for) {626input.parse::<kw::disable_for>()?;627let content;628let _ = braced!(content in input);629let items: Punctuated<FunctionField, Token![,]> =630content.parse_terminated(Parse::parse, Token![,])?;631let mut functions: HashMap<String, Vec<String>> = HashMap::new();632use std::collections::hash_map::Entry;633for i in items {634let function_names = i635.function_names636.iter()637.map(|i| i.to_string())638.collect::<Vec<String>>();639match functions.entry(i.module_name.to_string()) {640Entry::Occupied(o) => o.into_mut().extend(function_names),641Entry::Vacant(v) => {642v.insert(function_names);643}644}645}646647Ok(TracingConf {648enabled,649excluded_functions: functions,650})651} else {652Ok(TracingConf {653enabled,654excluded_functions: HashMap::new(),655})656}657}658}659660661