1 /// Execute the wiggle guest conversion code to exercise it
2 mod convert_just_errno {
3     use anyhow::Result;
4     use wiggle_test::{impl_errno, HostMemory, WasiCtx};
5 
6     /// The `errors` argument to the wiggle gives us a hook to map a rich error
7     /// type like this one (typical of wiggle use cases in wasi-common and beyond)
8     /// down to the flat error enums that witx can specify.
9     #[derive(Debug, thiserror::Error)]
10     pub enum RichError {
11         #[error("Invalid argument: {0}")]
12         InvalidArg(String),
13         #[error("Won't cross picket line: {0}")]
14         PicketLine(String),
15     }
16 
17     // Define an errno with variants corresponding to RichError. Use it in a
18     // trivial function.
19     wiggle::from_witx!({
20         witx_literal: "
21 (typename $errno (enum (@witx tag u8) $ok $invalid_arg $picket_line))
22 (module $one_error_conversion
23   (@interface func (export \"foo\")
24      (param $strike u32)
25      (result $err (expected (error $errno)))))
26     ",
27         errors: { errno => trappable ErrnoT },
28     });
29 
30     impl_errno!(types::Errno);
31 
32     impl From<RichError> for types::ErrnoT {
33         fn from(rich: RichError) -> types::ErrnoT {
34             match rich {
35                 RichError::InvalidArg(s) => {
36                     types::ErrnoT::from(types::Errno::InvalidArg).context(s)
37                 }
38                 RichError::PicketLine(s) => {
39                     types::ErrnoT::from(types::Errno::PicketLine).context(s)
40                 }
41             }
42         }
43     }
44 
45     impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> {
46         fn foo(&mut self, strike: u32) -> Result<(), types::ErrnoT> {
47             // We use the argument to this function to exercise all of the
48             // possible error cases we could hit here
49             match strike {
50                 0 => Ok(()),
51                 1 => Err(RichError::PicketLine(format!("I'm not a scab")))?,
52                 _ => Err(RichError::InvalidArg(format!("out-of-bounds: {}", strike)))?,
53             }
54         }
55     }
56 
57     #[test]
58     fn one_error_conversion_test() {
59         let mut ctx = WasiCtx::new();
60         let host_memory = HostMemory::new();
61 
62         // Exercise each of the branches in `foo`.
63         // Start with the success case:
64         let r0 = one_error_conversion::foo(&mut ctx, &host_memory, 0).unwrap();
65         assert_eq!(
66             r0,
67             types::Errno::Ok as i32,
68             "Expected return value for strike=0"
69         );
70         assert!(ctx.log.borrow().is_empty(), "No error log for strike=0");
71 
72         // First error case:
73         let r1 = one_error_conversion::foo(&mut ctx, &host_memory, 1).unwrap();
74         assert_eq!(
75             r1,
76             types::Errno::PicketLine as i32,
77             "Expected return value for strike=1"
78         );
79 
80         // Second error case:
81         let r2 = one_error_conversion::foo(&mut ctx, &host_memory, 2).unwrap();
82         assert_eq!(
83             r2,
84             types::Errno::InvalidArg as i32,
85             "Expected return value for strike=2"
86         );
87     }
88 }
89 
90 /// Type-check the wiggle guest conversion code against a more complex case where
91 /// we use two distinct error types.
92 mod convert_multiple_error_types {
93     pub use super::convert_just_errno::RichError;
94     use anyhow::Result;
95     use wiggle_test::{impl_errno, WasiCtx};
96 
97     /// Test that we can map multiple types of errors.
98     #[derive(Debug, thiserror::Error)]
99     #[allow(dead_code)]
100     pub enum AnotherRichError {
101         #[error("I've had this many cups of coffee and can't even think straight: {0}")]
102         TooMuchCoffee(usize),
103     }
104 
105     // Just like the prior test, except that we have a second errno type. This should mean there
106     // are two functions in UserErrorConversion.
107     // Additionally, test that the function "baz" marked noreturn always returns a wasmtime::Trap.
108     wiggle::from_witx!({
109         witx_literal: "
110 (typename $errno (enum (@witx tag u8) $ok $invalid_arg $picket_line))
111 (typename $errno2 (enum (@witx tag u8) $ok $too_much_coffee))
112 (module $two_error_conversions
113   (@interface func (export \"foo\")
114      (param $strike u32)
115      (result $err (expected (error $errno))))
116   (@interface func (export \"bar\")
117      (param $drink u32)
118      (result $err (expected (error $errno2))))
119   (@interface func (export \"baz\")
120      (param $drink u32)
121      (@witx noreturn)))
122     ",
123         errors: { errno => RichError, errno2 => AnotherRichError },
124     });
125 
126     impl_errno!(types::Errno);
127     impl_errno!(types::Errno2);
128 
129     // The UserErrorConversion trait will also have two methods for this test. They correspond to
130     // each member of the `errors` mapping.
131     // Bodies elided.
132     impl<'a> types::UserErrorConversion for WasiCtx<'a> {
133         fn errno_from_rich_error(&mut self, _e: RichError) -> Result<types::Errno> {
134             unimplemented!()
135         }
136         fn errno2_from_another_rich_error(
137             &mut self,
138             _e: AnotherRichError,
139         ) -> Result<types::Errno2> {
140             unimplemented!()
141         }
142     }
143 
144     // And here's the witx module trait impl, bodies elided
145     impl<'a> two_error_conversions::TwoErrorConversions for WasiCtx<'a> {
146         fn foo(&mut self, _: u32) -> Result<(), RichError> {
147             unimplemented!()
148         }
149         fn bar(&mut self, _: u32) -> Result<(), AnotherRichError> {
150             unimplemented!()
151         }
152         fn baz(&mut self, _: u32) -> anyhow::Error {
153             unimplemented!()
154         }
155     }
156 }
157