Skip to content

fix: generator fixes for keyword identifiers, null optionals and Default - #152

Merged
haraldh merged 1 commit into
varlink:masterfrom
lsjostro:feat/generator-safe-idents
Aug 9, 2026
Merged

fix: generator fixes for keyword identifiers, null optionals and Default#152
haraldh merged 1 commit into
varlink:masterfrom
lsjostro:feat/generator-safe-idents

Conversation

@lsjostro

Copy link
Copy Markdown
Contributor

Three varlink_generator fixes/improvements, found while generating client code for systemd's io.systemd.* interfaces (which use type and friends as field names liberally). They are bundled because they touch the same emission sites and share one regenerated golden file — happy to split if preferred.

1. Keywords that cannot be raw identifiers

The generator blindly prefixes every identifier with r#, but self, Self, crate and super cannot be raw identifiers — syn::parse_str("r#self") fails, so the generator panics on any interface using them as a type, field, variant or parameter name.

New safe_ident() suffixes those four with _ and pairs them with #[serde(rename = "...")] so the wire name is unchanged. All other names keep the existing r# treatment (no rename needed — serde already strips the r# prefix).

2. Null optionals in custom types

Method argument and reply structs already annotate optional fields with #[serde(skip_serializing_if = "Option::is_none")], but typedef structs don't. A minimally-populated custom type therefore serializes every absent field as null, which servers may reject — systemd, for example, treats an explicit null as an attempt to set a property rather than omitting it. Typedef structs now get the same annotation.

3. derive(Default)

All generated structs and enums now derive Default (enums mark the first variant #[default] — this only affects Enum::default(), never the wire encoding). This makes sparsely-populated argument structs ergonomic:

let ctx = UnitContext { description: Some("test".into()), ..Default::default() };

which matters for interfaces whose types carry dozens of optional fields.

Tests

tests/org.example.complex.varlink gains ReservedEnum, a Reserved type and a Baz method exercising the non-raw-able keywords as type, field, variant and method-parameter names; the golden output is regenerated (via varlink-rust-generator + rustfmt, minus the standalone-binary header lines).

Verified beyond the golden diff: generated code for a reserved-keywords interface compiles, self/Self fields round-trip through serde with the correct wire names, null optionals are omitted, and Enum::default() serializes as the first variant. (The org.example.complex golden itself doesn't compile as Rust — that's pre-existing on master, e.g. duplicate anonymous-struct emission for ErrorFoo_Args_enum — so the compile checks were done with a separate clean interface.)

cargo test -p varlink_generator, cargo test -p varlink, cargo clippy -p varlink_generator --all-targets -- -D warnings and cargo fmt --check pass.

@coveralls

coveralls commented Jul 24, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 31330034567

Coverage increased (+0.2%) to 56.477%

Details

  • Coverage increased (+0.2%) from the base build.
  • Patch coverage: 35 of 35 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 5427
Covered Lines: 3065
Line Coverage: 56.48%
Coverage Strength: 13.94 hits per line

💛 - Coveralls

@lsjostro

lsjostro commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

ping @haraldh 😄

@haraldh
haraldh enabled auto-merge August 9, 2026 18:43
@haraldh

haraldh commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Thank you!

@haraldh
haraldh disabled auto-merge August 9, 2026 18:48
@haraldh
haraldh enabled auto-merge August 9, 2026 18:49
Three varlink_generator fixes/improvements, found while generating code
for systemd's io.systemd.* interfaces:

- Identifiers: the generator blindly prefixes every name with `r#`, but
  `self`, `Self`, `crate` and `super` cannot be raw identifiers, so
  syn::parse_str panics on interfaces using them as field or variant
  names. New safe_ident() suffixes those four with `_` and pairs them
  with #[serde(rename = "...")] so the wire name is unchanged (raw
  identifiers need no rename; serde already strips the r# prefix).

- Custom types (typedef structs) now annotate optional fields with
  #[serde(skip_serializing_if = "Option::is_none")], matching how method
  argument and reply structs are already emitted. Without this, a
  minimally-populated custom type serializes every absent field as
  `null`, which servers may reject (e.g. systemd treats null as an
  attempt to set an unsettable property).

- All generated structs and enums derive Default (enums mark the first
  variant #[default]), so callers can construct sparsely-populated
  argument structs with `..Default::default()` — practical for
  interfaces whose types carry many optional fields.

tests/org.example.complex.varlink gains ReservedEnum, a Reserved type
and a Baz method exercising the non-raw-able keywords as type, field,
variant and method-parameter names; the golden output is regenerated
accordingly.
@haraldh
haraldh force-pushed the feat/generator-safe-idents branch from 52d4825 to 954a631 Compare August 9, 2026 18:49
@haraldh
haraldh merged commit c215bf1 into varlink:master Aug 9, 2026
14 checks passed
@lsjostro

Copy link
Copy Markdown
Contributor Author

now it's easy to consume this crate with bazel. https://registry.bazel.build/modules/rules_varlink . Thanks @haraldh!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants