-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support components result
types
#391
Conversation
Type::Result(result_type) => { | ||
// Expect value to conform to `Wasmtime::Component::Value`'s interface | ||
let ruby = Ruby::get().unwrap(); | ||
let is_ok = value.funcall::<_, (), bool>(IS_OK.into_id_with(&ruby), ())?; | ||
|
||
if is_ok { | ||
let ok_value = value.funcall::<_, (), Value>(OK.into_id_with(&ruby), ())?; | ||
match result_type.ok() { | ||
Some(ty) => rb_to_component_val(ok_value, _store, &ty) | ||
.map(|val| Val::Result(Result::Ok(Some(Box::new(val))))), | ||
None => { | ||
if ok_value.is_nil() { | ||
Ok(Val::Result(Ok(None))) | ||
} else { | ||
err!( | ||
"expected nil for result<_, E> ok branch, got {}", | ||
ok_value.inspect() | ||
) | ||
} | ||
} | ||
} | ||
} else { | ||
let err_value = value.funcall::<_, (), Value>(ERROR.into_id_with(&ruby), ())?; | ||
match result_type.err() { | ||
Some(ty) => rb_to_component_val(err_value, _store, &ty) | ||
.map(|val| Val::Result(Result::Err(Some(Box::new(val))))), | ||
None => { | ||
if err_value.is_nil() { | ||
Ok(Val::Result(Err(None))) | ||
} else { | ||
err!( | ||
"expected nil for result<O, _> error branch, got {}", | ||
err_value.inspect() | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a mouthful. I tried simplifying but did not come up with anything much better. Ideas are welcome.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When converting Ruby Result to a result branch of a none type (e.g. unparametrized result), the Ruby value must be nil. This follows the same spirit that the Ruby bindings generally avoids type casting as a way to avoid subtle bugs.
Example: converting Result.ok(1) to a component model result<_, E> will error. Note that _ here means "unit".
IMHO, this is an important design decision that should probably be documented somewhere in the public API/docs? Maybe the result class is a good place to start?
if ok_value.is_nil() { | ||
Ok(Val::Result(Ok(None))) | ||
} else { | ||
err!( | ||
"expected nil for result<_, E> ok branch, got {}", | ||
ok_value.inspect() | ||
) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This invariant is the same-ish in both cases (ok, error), perhaps this could be extracted into a local function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this more or less what you had in mind?
@@ -185,38 +198,40 @@ pub(crate) fn rb_to_component_val(
// Expect value to conform to `Wasmtime::Component::Value`'s interface
let ruby = Ruby::get().unwrap();
let is_ok = value.funcall::<_, (), bool>(IS_OK.into_id_with(&ruby), ())?;
+ let expect_nil = |value: Value,
+ nil_case: Val,
+ error_handler: fn(Value) -> Result<Val, Error>|
+ -> Result<Val, Error> {
+ if value.is_nil() {
+ Ok(nil_case)
+ } else {
+ error_handler(value)
+ }
+ };
if is_ok {
let ok_value = value.funcall::<_, (), Value>(OK.into_id_with(&ruby), ())?;
match result_type.ok() {
Some(ty) => rb_to_component_val(ok_value, _store, &ty)
.map(|val| Val::Result(Result::Ok(Some(Box::new(val))))),
- None => {
- if ok_value.is_nil() {
- Ok(Val::Result(Ok(None)))
- } else {
- err!(
- "expected nil for result<_, E> ok branch, got {}",
- ok_value.inspect()
- )
- }
- }
+ None => expect_nil(ok_value, Val::Result(Ok(None)), |v| {
+ err!(
+ "expected nil for result<_, E> ok branch, got {}",
+ v.inspect()
+ )
+ }),
}
} else {
let err_value = value.funcall::<_, (), Value>(ERROR.into_id_with(&ruby), ())?;
match result_type.err() {
Some(ty) => rb_to_component_val(err_value, _store, &ty)
.map(|val| Val::Result(Result::Err(Some(Box::new(val))))),
- None => {
- if err_value.is_nil() {
- Ok(Val::Result(Err(None)))
- } else {
- err!(
- "expected nil for result<O, _> error branch, got {}",
- err_value.inspect()
- )
- }
- }
+ None => expect_nil(err_value, Val::Result(Err(None)), |v| {
+ err!(
+ "expected nil for result<O, _> error branch, got {}",
+ v.inspect()
+ )
+ }),
}
}
}
I preferred the prior version; this one doesn't communicate the intent much better but adds indirection. If you can think of a better approach, please share 😄.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm good keeping the previous version!
Agreed, this decision should be documented. A doc section on component model <-> Ruby type conversion is necessary. I think the most natural place for it to go is in the API doc for I'll make those 2 changes (introduce |
* Do not remap Hash conversion error to Wasmtime::Error, for consistency with other convert error types. * Treat missing fields as such, instead of converting to nil. This will lead to better error messages.
c3c9e9e
to
fa0aa9f
Compare
Add support for the component model's result type.
result<O, E>
are mapped to instances ofWasmtime::Component::Result
. I intentionally left the interface simple, but we can expand it if we see fit.Some design choices:
Result
behave like value objects for equality and hashing, similar to hash, array, etc.Result
is implemented in Ruby for simplicty.Result
to a result branch of a none type (e.g. unparametrizedresult
), the Ruby value must be nil. This follows the same spirit that the Ruby bindings generally avoids type casting as a way to avoid subtle bugs.Example: converting
Result.ok(1)
to a component modelresult<_, E>
will error. Note that_
here means "unit".This PR also includes slight non-controversial tweaks to tests and conversion logic of records.