-
Notifications
You must be signed in to change notification settings - Fork 47
IDL gen improvements #233
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
base: main
Are you sure you want to change the base?
IDL gen improvements #233
Conversation
4b345c8
to
045300f
Compare
Ouch. Trying to parse the Whitespace is significant after identifier and numeric tokens in that if the following token is not a punctuation / operation token (i.e. a /*, /** or // comment or a ",;<>[]="), it needs to have at least one whitespace character before proceeding to the next token, which might itself be an identifier or a numeric token. The net result is that the parser currently parses
as
Usually, having a separate lexer/tokenizer that works on the character level and emits a higher-level Alas, our parser does not have a formal lexer, so |
045300f
to
c017b37
Compare
OK. The whitespace bug is now fixed in commit 6c46c6a by replacing all
The change is small but looks large as I had to regexp almost all usages of the non-greedy I'm however now having second thoughts (sorry Andrey!) if using the Anyway, I've captured my thoughts here: #234 Still, to be also checked if parsing the IDL is the thing dominating the proc-macro. It might well be that the code-generation is not very efficient as well, given how big/comprehensive it had become, or it might be that rustc compiling the generated code is not very efficient either. |
6ca5baa
to
ee00ba4
Compare
2599b39
to
03daa3a
Compare
03daa3a
to
6178f15
Compare
…nightly; restore the modules for generated clusters
624820b
to
ffedf69
Compare
ffedf69
to
4dbc9bd
Compare
… and AsyncHandler methods signatures
bb36543
to
b14adbb
Compare
b14adbb
to
88bf6d8
Compare
@kedars This is now ready for review. I'm sorry for the relatively large changeset outside of Net-net I'm quite happy though. Look at the resurrected
Note that there will be three additional PRs upcoming (as this one grew quite large anyway):
... and then of course resuming the work on C++ Python / XML tests, which was the reason why this work was initiated originally. As in, implementing the UnitTesting cluster manually is very difficult and not worth it. |
@kedars One more note:
if you look at the speaker
example, the import!
ed cluster-handlers have method signatures for each command defined in the cluster.
Since the commands (unlike attributes) are not marked as "optional" in the IDL, we cannot really provide default "Unimplemented command" impl for "optional" commands which the user might not want to implement in its cluster handler. Therefore currently methods for ALL commands are generated and the user is expected to throw an "Unimplemented command" error in the command handlers which are not enumerated as supported in the cluster meta-data.
An alternative to the above would be to provide default method impls throwing "Unimplemented command" for ALL cluster commands, and then the user can override only those methods, which are for commands its cluster implements. This would cut down the verbosity of the cluster impl a bit, but I'm unsure about that. What do you think?
An even third option would be to enhance the import!
macro a bit so that the user can provide a list of to-be implemented attributes / commands (and a cluster revision) and then the cluster handler and its cluster meta-data would be generated to contain only the user-specified attributes/commands. I'm unsure about that though as it would complicate the proc-macro generator even more. Perhaps, we can introduce this at a later stage, if deemed necessary.
This PR is finishing the work @andy31415 initiated ~ 1 year ago.
NOTE: It is on-top of PR #232 which needs to be merged first.(
PR still in draft, because(a)
unit tests are not updated and failDone(b)
Doneidl.rs
needs to be split as the code inside it grew considerably)
The TL;DR is: we are now able to generate - from the IDL - artefacts for everything contained in a server cluster IDL definition!
Enums and (bit)sets
This were already available
Simple enums for attribute and command IDs (
AttrributeId
,CommandId
andCommandResponseId
)Work was almost done, I just completed it
(NEW) "Thin" Structures
The generation of structures was incomplete as it couldn't handle nested structs, arrays, nullables and optionals.
The generation of structures was totally revamped though, as we now generate different types of structs than the original idea.
These two types of structs are generated regardless whether the struct is a general one, or a request, or a response one (so that we can support matter-controller/client scenarios in addition to matter-server, as we do now).
Structs for incoming data
Assuming a
...struct in the IDL, a
... type is generated in Rust.
This type of struct has a minimal footprint (the size of a wide pointer in Rust) in that it does all parsing of its field members "on the fly", when the method of the struct modeling the relevant field member is called by the user.
In other words, we trade extra computation for less memory footprint.
In future - and if deemed necessary - these generated structs might get an extra
expand
method that returns an "expanded" traditionalFromTLV
struct which contains actual fields (but has a much bigger memory footprint).Structs for outgoing data (builders)
Assuming the IDL
FooStruct
from above, the proc-macro will generate aFooStructBuilder
as well.This type is just a builder pattern for the corresponding struct, where the struct can be created incrementally, by writing its members into a
TLVWrite
trait instance in a "type-safe" way.This is also done as a way to consume minimal memory, as the Builder is just a wide pointer as well (a
mut
writer reference) and the writer writes directly into the outgoing TX packet.This and the previous pattern can be observed "in action" in the
cluster_basic_information
,general_commissioning
,cluster_on_off
and a few others which were only partially converted to IDL-generated code, but are now completely based on IDL-generated code.(NEW)
CLUSTER
cluster Meta-DataThe
static CLUSTER: Cluster<'static> = Cluster {}
meta-data describing the cluster attributes and commands is now completely auto-generated as well. Used by the framework for path expansion, permissions checking and others.(NEW) Cluster-specific Handlers
While we have general sync and async cluster handlers in the form:
...these are obviously very generic in that their layout is not tailored to the concrete cluster which the user needs to implement, with its concrete attributes and commands.
So now, the proc-macro generates cluster-specific handlers which are much more straight-forward in terms of implementation.
Here's the implementation of the General Commissioning cluster, using the auto-generated
GeneralCommissioningHandler
:Also note how the methods corresponding to commands and attributes take our minimal
TLVElement
-wrapping structs as input, and expect *Builder structs as output.Additionally, the proc-macro generates an adaptor struct, that adapts e.g. a
GeneralCommissioningHandler
trait implementation to the genericHandler
implementation from above, that thers-matter
IM/DM layer understands.Why is the changeset so big?
The changes outside of
rs-matter-macros-impl
are actually small and relatively incremental. They are everywhere though, as I had to touch several cross-cutting aspects:Most
info!
logging lowered todebug!
Outputting
rs-matter
packets should not be with levelinfo!
, asinfo!
should be the default mode of operation ofrs-matter
. Moreover, we now log even more stuff - as in, the proc-macro-generated cluster-adaptors are logging their attribute and command input data, as well as the output data, which is very handy, but makes the logging even more verbose. This is all nowdebug!
logging, as it should be.Macro-renames:
cluster_attrs
andsupported_attributes
combined and renamed toattributes!
,supported_commands!
/generated_commands!
combined and renamed to justcommands!
The layout of the
Cluster
meta-data struct was changed slightly to accommodate the newCommand
type (similar to the existingAttribute
. We need it so that we can describe the required access for each command, which was a long-standing TODO.As a result, it made sense to simplify and unify the existing macros.
Handler
/AsyncHandler
traits' methods simplified withReadContext
,WriteContext
andInvokeContext
The method signatures of our handlers (this is valid for the new proc-macro-generated handlers too) were becoming a bit unwieldy with too many parameters.
The above
*Context
structures combine what used to be separate method parameters into a single structure.Notifying subscribes on an attribute change from within the cluster handler now works
Turns out, this wasn't possible before, because our handlers simply did not have access to the
Subscriptions
struct. Now possible and one more reason for the*Context
variables from above.Examples streamlined;
speaker
example resurrectedThe
speaker
example (MediaPlayback
cluster impl) was sitting commented out for > 1 year, as I was not so sure we have to "manually" implement "helper" clusters for this and that, as the example did.With the new proc-macro this is now not necessary. The
speaker
example showcases how users can use theimport!
proc-macro in their own code, outside ofrs-matter
so as to get all definitions for a cluster, and implement the cluster-specific handler.Also, the examples are streamlined in their own
examples
project now. They are also simplified in that we do not need to talk/document theno_std
use cases, as these are taken over byrs-matter-embassy
.