Skip to content

Replace combinatorial decorators with capability registry#28116

Draft
Turbo87 wants to merge 6 commits intoevcc-io:masterfrom
Turbo87:caps
Draft

Replace combinatorial decorators with capability registry#28116
Turbo87 wants to merge 6 commits intoevcc-io:masterfrom
Turbo87:caps

Conversation

@Turbo87
Copy link
Contributor

@Turbo87 Turbo87 commented Mar 11, 2026

As mentioned in #28113 (comment), the current decorator code leads to a combinatorial explosion when new interfaces are added. This PR is an attempt to resolve the issue and make it scale linearly instead.

Disclaimer: I've used Claude Code to create this branch/PR and my knowledge of the codebase and Go is limited. Feel free to close the PR and tell me that this is a stupid approach and is missing some important aspects 😅

I only did a quick manual test to verify that it works conceptually and the test suite appears to work fine with this approach. I can't tell if this has a significant performance impact though, or whether that would actually be relevant enough for this project.

TODO

  • adopt decorate.go
  • generate all assets

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @Turbo87, your pull request is larger than the review limit of 150000 diff characters

@Turbo87
Copy link
Contributor Author

Turbo87 commented Mar 11, 2026

well, looks like CI revealed some issues. I'll take a look :)

@andig
Copy link
Member

andig commented Mar 11, 2026

This does actually look like a clean option. The not-so-nice part is having to distinguish when to go for straight type assertion and when to go through api.Cap() which is not quite go-ish. But it does seem like a very straightforward thing to do. Lets have it simmer for a while to form an opinion.

/cc @Maschga

Turbo87 added 3 commits March 11, 2026 17:26
Add `api.Cap[T]` generic helper and `api.Capable` interface for
dynamic capability lookup. Replace the 12,443-line generated
`vehicle_decorators.go` (448 switch cases for 2^10 interface
combinations) with a 151-line capability-map approach that grows
O(n) instead of O(2^n).

Migrate all vehicle-related type assertion sites (42 occurrences
across 13 files) from `v.(api.X)` to `api.Cap[api.X](v)`.
The `Cap` helper tries direct type assertion first (fast path
for non-decorated concrete types), then falls back to the
`Capable` registry for decorated types.
Replace the 12,137-line generated `charger_decorators.go` (432 switch
cases for 2^10 interface combinations) with a 160-line capability-map
approach.

Migrate charger-related type assertion sites from `v.(api.X)` to
`api.Cap[api.X](v)` across core/, server/, cmd/, and charger test
files.
Replace the 1,197-line generated `meter_decorators.go` (56 switch
cases across `decorateMeter` and `decorateMeterBattery`) with a
197-line capability-map approach.

Migrate meter-related type assertion sites from `v.(api.X)` to
`api.Cap[api.X](v)` across core/, meter/, and plugin/ files.
@andig
Copy link
Member

andig commented Mar 11, 2026

@Turbo87 there's no point doing any replacements in the generated code one by one- the generator needs be fixed...

@Turbo87
Copy link
Contributor Author

Turbo87 commented Mar 11, 2026

alright 👍


// ChargeRater interface should NOT be available when parameters are not supported
_, ok := wb2.(api.ChargeRater)
_, ok := api.Cap[api.ChargeRater](wb2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add an additional HasCap[T] if only interested in the existence?

}

if _, ok := instance.(api.BatteryController); ok {
if _, ok := api.Cap[api.BatteryController](instance); ok {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use HasCap[T]

@andig
Copy link
Member

andig commented Mar 14, 2026

@Maschga would you potentially look into the decorator? I couldn't find a quick solution and gave up after an hour :O

assert.Equal(t, 99.0, energy)

// should NOT find PhaseCurrents (not registered)
_, ok = Cap[PhaseCurrents](decorated)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use HasCap

@Maschga
Copy link
Collaborator

Maschga commented Mar 15, 2026

@Maschga would you potentially look into the decorator? I couldn't find a quick solution and gave up after an hour :O

I can take a look at that. It’ll take a while, though.

@andig andig added infrastructure Basic functionality backlog Things to do later labels Mar 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backlog Things to do later infrastructure Basic functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants