-
Notifications
You must be signed in to change notification settings - Fork 12
Description
In some cases, it may be useful for an Idol server to only provide certain IPCs when a Cargo feature flag is enabled.
For a motivating example, see oxidecomputer/hubris#2126 (comment) --- we would like to be able to feature-flag packrat's ereport storage interface, so that the 4KiB ereport buffer need not exist on tiny boards. Presently, the approach is just to make the IPCs that require a feature flag on the server task fail with ClientError::UnknownOperation when the server's feature is not enabled. But, this is a bit unfortunate, as it results in a runtime fault when the client tries to use features of the server that were not enabled at build time. It would be much nicer if this could be checked at build time: the task-packrat-api crate could have a corresponding feature flag and a build script that checks whether the server task's "ereport" feature flag is enabled if the client enables that feature. Unfortunately, though, because the API functions that the client actually uses to perform those IPCs are generated by Idol codegen, the interfaces can't currently be made to require the feature flag, and the client can always call them without enabling the feature on the API crate. So, the build-script checking doesn't actually help that much --- it's still possible to forget to enable the feature and call unsupported IPCs.
It would be nice if the IDL could specify that an IPC requires a feature flag, and generate code with a #[cfg(feature = ...)] attribute in the client API crate and server stub. Since we might reasonably want to use more complex logic in cfg attributes for IPCs (e.g. "enabled if feature A or feature B), we could just go ahead and accept the entire body of the cfg attribute, rather than a specific feature name.
Something like this:
Interface(
name: "Packrat",
ops: {
// ...
"deliver_ereport": (
doc: "Hand an encoded ereport to packrat for buffering.",
args: {
},
leases: {
"data": (type: "[u8]", read: true, max_len: Some(1024)),
},
reply: Simple("()"),
idempotent: true,
// this is the new syntax
cfg: "feature = \"ereport\"",
),
},
)which would generate something like this in the client stub:
/// operation: deliver_ereport (11)
/// idempotent - will auto-retry
#[cfg(feature = "ereport")]
pub fn deliver_ereport(&self, data: &[u8]) -> () {
// ...
}This is probably a little bit annoying to implement, since all the code generated for a cfg-gated interface would need to have the cfg attribute, but probably not actually difficult.