Context:
+Filip Filmar and I have talked about FIDL types vs. implementation-specific types before. This email describes a case where I think using a FIDL type within an implementation makes the code harder to maintain.
Right now, we have ad-hoc validation code to ensure that KeyEvents emitted by the IME service a) contain a timestamp, and b) contain either a Key or a KeyMeaning. We have to do this validation ad-hoc because a) we use the KeyEvent FIDL type internally in IME service, and b) the FIDL type uses tables, which makes `timestamp`, `key`, and `key_meaning` Option-al in the Rust bindings.
If we used a custom type instead, we could get static checking in most of the implementation, and idiomatic validation when receiving the FIDL type. See details below.
Filip, WDYT?
use fidl_fuchsia... as FidlKeyboardEvent;
use fidl_fuchsia... as FidlKeyMeaning;
use fidl_fuchsia... as FidlKey;
enum KeyData {
// Only the key is available. This might be, e.g., because
// the data is from a physical keyboard, and no keyboard
// layout has been applied.
KeyOnly(FidlKey),
// Only the meaning is available. This might be, e.g., because
// the data is from a virtual keyboard, and no reverse layout
// has been applied.
MeaningOnly(FidlKeyMeaning),
// Both the key and its meaning are available.
KeyAndMeaning(FidlKey, FidlKeyMeaning),
}
struct KeyboardEvent {
timestamp: i32,
key_data: KeyData,
// ... other fields omitted
}
// Note: requires Rust 1.4.1. If we're using an older version, then
// `impl std::convert::Into<FidlKeyboardEvent> for KeyboardEvent`
// instead.
impl std::convert::From<KeyboardEvent> for FidlKeyboardEvent {
fn from(evt: KeyboardEvent) -> FidlKeyboardEvent {
let (key, key_meaning) = match evt.key_data {
KeyData::KeyOnly(k) => (Some(k), None),
KeyData::MeaningOnly(m) => (None, Some(m)),
KeyData::KeyAndMeaning(k, m) => (Some(k), Some(m)),
};
FidlKeyboardEvent {
timestamp: Some(evt.timestamp),
key,
key_meaning,
// ... other fields omitted
}
}
}
impl std::convert::TryFrom<FidlKeyboardEvent> for KeyboardEvent {
fn try_from(evt: FidlKeyboardEvent) ->
Result<KeyboardEvent, Error> {
let key_data = match (evt.key, evt.key_meaning) {
(Some(k), Some(m)) => KeyData::KeyAndMeaning(k, m),
(Some(k), None) => KeyData::KeyOnly(k),
(None, Some(m)) => KeyData::MeaningOnly(m),
(None, None) => format_err!("no key or meaning")?,
};
KeyboardEvent {
timestamp: evt.timestamp,
key_data,
// ... other fields omitted
}
}
}