better error trait design

This commit is contained in:
Pat Hickey
2020-01-23 11:21:04 -08:00
parent 7cc0073a3e
commit cb24fd97c0
5 changed files with 29 additions and 72 deletions

View File

@@ -1,57 +0,0 @@
use crate::names::Names;
use heck::SnakeCase;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use std::collections::HashSet;
use witx::{Document, TypeRef};
/// The context struct needs to implement a trait for converting memory and value errors into the
/// witx doc's error types.
///
// XXX im rethinking this. maybe each error type should impl
// pub trait WitxErrorType {
// type Context;
// fn is_success(&self) -> bool;
// fn from_memory_error(memory_error: MemoryError, ctx: &mut Context) -> Self;
// fn from_value_error(value_error: GuestValueError, ctx: &mut Context) -> Self;
// }
//
// where Context is mapped to their wasi ctx.
// It seems less "magic" to leave that impl up to the user, and the error message may be simpler?
//
pub fn define_error_trait(names: &Names, doc: &Document) -> TokenStream {
// All non-anonymous first return types are used to pass errors.
let error_typenames = doc
.modules()
.flat_map(|m| {
m.funcs()
.filter_map(|f| {
f.results.get(0).and_then(|r| match &r.tref {
TypeRef::Name(nt) => Some(nt.name.clone()),
_ => None,
})
})
.collect::<HashSet<witx::Id>>()
})
.collect::<HashSet<witx::Id>>();
let methods = error_typenames.iter().map(|typename| {
let tname = names.type_(typename);
let methodfragment = typename.as_str().to_snake_case();
let success = format_ident!("success_to_{}", methodfragment);
let memory_error = format_ident!("memory_error_to_{}", methodfragment);
let value_error = format_ident!("value_error_to_{}", methodfragment);
quote! {
fn #success(&mut self) -> #tname;
fn #memory_error(&mut self, err: ::memory::MemoryError) -> #tname;
fn #value_error(&mut self, err: ::memory::GuestValueError) -> #tname;
}
});
quote!(
pub trait WitxErrorConversion {
#(#methods)*
}
)
}

View File

@@ -1,6 +1,5 @@
extern crate proc_macro; extern crate proc_macro;
mod errors;
mod funcs; mod funcs;
mod names; mod names;
mod parse; mod parse;
@@ -10,7 +9,6 @@ use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::quote;
use errors::define_error_trait;
use funcs::define_func; use funcs::define_func;
use names::Names; use names::Names;
use types::define_datatype; use types::define_datatype;
@@ -39,12 +37,9 @@ pub fn from_witx(args: TokenStream) -> TokenStream {
) )
}); });
let error_trait = define_error_trait(&names, &doc);
TokenStream::from(quote!( TokenStream::from(quote!(
mod types { mod types {
#(#types)* #(#types)*
#error_trait
} }
#(#modules)* #(#modules)*
)) ))

View File

@@ -1,4 +1,4 @@
use crate::{GuestPtr, GuestPtrMut}; use crate::{GuestPtr, GuestPtrMut, MemoryError};
use thiserror::Error; use thiserror::Error;
pub trait GuestType: Sized { pub trait GuestType: Sized {
@@ -66,3 +66,10 @@ macro_rules! builtin_copy {
// These definitions correspond to all the witx BuiltinType variants that are Copy: // These definitions correspond to all the witx BuiltinType variants that are Copy:
builtin_copy!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize, char); builtin_copy!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize, char);
pub trait GuestError {
type Context;
fn is_success(&self) -> bool;
fn from_memory_error(memory_error: MemoryError, ctx: &mut Self::Context) -> Self;
fn from_value_error(value_error: GuestValueError, ctx: &mut Self::Context) -> Self;
}

View File

@@ -3,6 +3,6 @@ mod guest_type;
mod memory; mod memory;
mod region; mod region;
pub use guest_type::{GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError}; pub use guest_type::{GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError};
pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, MemoryError}; pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, MemoryError};
pub use region::Region; pub use region::Region;

View File

@@ -1,18 +1,30 @@
pub mod test { pub mod test {
// FIXME: parameterize macro on what ctx type is used here
generate::from_witx!("test.witx"); generate::from_witx!("test.witx");
pub struct WasiCtx {} // FIXME: parameterize macro on what ctx type is used here pub struct WasiCtx {
mem_errors: Vec<::memory::MemoryError>,
impl types::WitxErrorConversion for WasiCtx { value_errors: Vec<::memory::GuestValueError>,
fn success_to_errno(&mut self) -> types::Errno {
types::Errno::Ok
} }
fn memory_error_to_errno(&mut self, e: ::memory::MemoryError) -> types::Errno {
eprintln!("memory error: {:?}", e); // Errno is used as a first return value in the functions above, therefore
// it must implement GuestError with type Context = WasiCtx.
// The context type should let you do logging or debugging or whatever you need
// with these errors. We just push them to vecs.
impl ::memory::GuestError for types::Errno {
type Context = WasiCtx;
fn is_success(&self) -> bool {
match self {
types::Errno::Ok => true,
_ => false,
}
}
fn from_memory_error(e: ::memory::MemoryError, ctx: &mut WasiCtx) -> types::Errno {
ctx.mem_errors.push(e);
types::Errno::InvalidArg types::Errno::InvalidArg
} }
fn value_error_to_errno(&mut self, e: ::memory::GuestValueError) -> types::Errno { fn from_value_error(e: ::memory::GuestValueError, ctx: &mut WasiCtx) -> types::Errno {
eprintln!("guest value error: {:?}", e); ctx.value_errors.push(e);
types::Errno::InvalidArg types::Errno::InvalidArg
} }
} }