Skip to content

Commit 8d9839a

Browse files
Copilotkennykerr
andauthored
feat(rdl): add property and event shorthand syntax for interfaces (#4143)
Co-authored-by: Kenny Kerr <kenny@kennykerr.ca> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: kennykerr <9845234+kennykerr@users.noreply.github.com>
1 parent 0c719dc commit 8d9839a

File tree

7 files changed

+445
-76
lines changed

7 files changed

+445
-76
lines changed

crates/libs/rdl/src/reader/interface.rs

Lines changed: 259 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::guid;
22
use super::*;
33

44
syn::custom_keyword!(interface);
5+
syn::custom_keyword!(event);
56

67
#[derive(Debug)]
78
pub struct Interface {
@@ -10,10 +11,76 @@ pub struct Interface {
1011
pub name: syn::Ident,
1112
pub generics: syn::Generics,
1213
pub requires: Vec<syn::Path>,
13-
pub methods: Vec<Method>,
14+
pub members: Vec<InterfaceMember>,
1415
pub winrt: bool,
1516
}
1617

18+
/// A property shorthand inside an interface body.
19+
///
20+
/// Syntax: `[#[get] | #[set]] Name: Type;`
21+
///
22+
/// - No attribute → generates both `get_Name` and `put_Name` methods.
23+
/// - `#[get]` → generates only `get_Name`.
24+
/// - `#[set]` → generates only `put_Name`.
25+
#[derive(Debug)]
26+
pub struct Property {
27+
pub attrs: Vec<syn::Attribute>,
28+
pub name: syn::Ident,
29+
pub ty: syn::Type,
30+
}
31+
32+
/// An event shorthand inside an interface body.
33+
///
34+
/// Syntax: `event Name: HandlerType;`
35+
///
36+
/// Generates `add_Name` and `remove_Name` methods with the `SpecialName` flag.
37+
#[derive(Debug)]
38+
pub struct Event {
39+
pub name: syn::Ident,
40+
pub handler_ty: syn::Type,
41+
}
42+
43+
/// A member of an interface body: either a regular method, a property shorthand,
44+
/// or an event shorthand.
45+
#[derive(Debug)]
46+
pub enum InterfaceMember {
47+
Method(Method),
48+
Property(Property),
49+
Event(Event),
50+
}
51+
52+
impl syn::parse::Parse for InterfaceMember {
53+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
54+
// Peek past any outer attributes to determine which member kind follows.
55+
let fork = input.fork();
56+
fork.call(syn::Attribute::parse_outer)?;
57+
58+
if fork.peek(syn::Token![fn]) {
59+
return input.parse().map(Self::Method);
60+
}
61+
62+
if fork.peek(event) {
63+
// Consume (and discard) any attributes before `event` — the event
64+
// shorthand does not support per-member attributes.
65+
input.call(syn::Attribute::parse_outer)?;
66+
let _event_token: event = input.parse()?;
67+
let name: syn::Ident = input.parse()?;
68+
input.parse::<syn::Token![:]>()?;
69+
let handler_ty: syn::Type = input.parse()?;
70+
input.parse::<syn::Token![;]>()?;
71+
return Ok(Self::Event(Event { name, handler_ty }));
72+
}
73+
74+
// Property shorthand: `[#[get] | #[set]] Name: Type;`
75+
let attrs = input.call(syn::Attribute::parse_outer)?;
76+
let name: syn::Ident = input.parse()?;
77+
input.parse::<syn::Token![:]>()?;
78+
let ty: syn::Type = input.parse()?;
79+
input.parse::<syn::Token![;]>()?;
80+
Ok(Self::Property(Property { attrs, name, ty }))
81+
}
82+
}
83+
1784
impl syn::parse::Parse for Interface {
1885
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
1986
let attrs = input.call(syn::Attribute::parse_outer)?;
@@ -33,10 +100,10 @@ impl syn::parse::Parse for Interface {
33100

34101
let content;
35102
syn::braced!(content in input);
36-
let mut methods = vec![];
103+
let mut members = vec![];
37104

38105
while !content.is_empty() {
39-
methods.push(content.parse()?);
106+
members.push(content.parse()?);
40107
}
41108

42109
Ok(Self {
@@ -45,7 +112,7 @@ impl syn::parse::Parse for Interface {
45112
name,
46113
generics,
47114
requires,
48-
methods,
115+
members,
49116
winrt: false,
50117
})
51118
}
@@ -123,86 +190,211 @@ impl Encoder<'_> {
123190

124191
let mut method_signatures: Vec<(String, Vec<metadata::Type>, metadata::Type)> = Vec::new();
125192

126-
for method in &item.methods {
127-
let mut params = vec![];
193+
for member in &item.members {
194+
match member {
195+
InterfaceMember::Method(method) => {
196+
let mut params = vec![];
128197

129-
if method.sig.inputs.is_empty() {
130-
return self.err(&method.sig.ident, "`&self` parameter not found");
131-
}
198+
if method.sig.inputs.is_empty() {
199+
return self.err(&method.sig.ident, "`&self` parameter not found");
200+
}
132201

133-
for (sequence, arg) in method.sig.inputs.iter().enumerate() {
134-
match arg {
135-
syn::FnArg::Receiver(receiver) => {
136-
if *receiver != syn::parse_quote! { &self } {
137-
return self.err(receiver, "`&self` parameter not found");
202+
for (sequence, arg) in method.sig.inputs.iter().enumerate() {
203+
match arg {
204+
syn::FnArg::Receiver(receiver) => {
205+
if *receiver != syn::parse_quote! { &self } {
206+
return self.err(receiver, "`&self` parameter not found");
207+
}
208+
}
209+
syn::FnArg::Typed(pt) => {
210+
if sequence == 0 {
211+
return self.err(arg, "`&self` parameter not found");
212+
}
213+
let p = self.param(pt)?;
214+
if item.winrt {
215+
self.validate_type_is_winrt(&pt.ty, &p.ty)?;
216+
}
217+
params.push(p);
218+
}
138219
}
139220
}
140-
syn::FnArg::Typed(pt) => {
141-
if sequence == 0 {
142-
return self.err(arg, "`&self` parameter not found");
221+
222+
let types: Vec<metadata::Type> =
223+
params.iter().map(|param| param.ty.clone()).collect();
224+
let return_type = self.encode_return_type(&method.sig.output)?;
225+
226+
if item.winrt {
227+
if let syn::ReturnType::Type(_, return_syn_ty) = &method.sig.output {
228+
self.validate_type_is_winrt(return_syn_ty.as_ref(), &return_type)?;
143229
}
144-
let p = self.param(pt)?;
145-
if item.winrt {
146-
self.validate_type_is_winrt(&pt.ty, &p.ty)?;
230+
}
231+
232+
if !already_has_guid {
233+
method_signatures.push((
234+
method.sig.ident.to_string(),
235+
types.clone(),
236+
return_type.clone(),
237+
));
238+
}
239+
240+
let signature = metadata::Signature {
241+
flags: metadata::MethodCallAttributes::HASTHIS,
242+
return_type,
243+
types,
244+
};
245+
246+
let mut is_special = false;
247+
for attr in &method.attrs {
248+
if attr.path().is_ident("special") {
249+
if !matches!(attr.meta, syn::Meta::Path(_)) {
250+
return self
251+
.err(attr, "`special` attribute does not accept arguments");
252+
}
253+
is_special = true;
147254
}
148-
params.push(p);
149255
}
150-
}
151-
}
152256

153-
let types: Vec<metadata::Type> = params.iter().map(|param| param.ty.clone()).collect();
154-
let return_type = self.encode_return_type(&method.sig.output)?;
257+
let mut flags = base_flags;
258+
if is_special {
259+
flags |= metadata::MethodAttributes::SpecialName;
260+
}
155261

156-
if item.winrt {
157-
if let syn::ReturnType::Type(_, return_syn_ty) = &method.sig.output {
158-
self.validate_type_is_winrt(return_syn_ty.as_ref(), &return_type)?;
262+
let method_def = self.output.MethodDef(
263+
&method.sig.ident.to_string(),
264+
&signature,
265+
flags,
266+
Default::default(),
267+
);
268+
269+
self.encode_attrs(
270+
metadata::writer::HasAttribute::MethodDef(method_def),
271+
&method.attrs,
272+
&["special"],
273+
)?;
274+
275+
self.encode_return_attrs(&method.return_attrs)?;
276+
self.encode_params(&params)?;
159277
}
160-
}
161278

162-
if !already_has_guid {
163-
method_signatures.push((
164-
method.sig.ident.to_string(),
165-
types.clone(),
166-
return_type.clone(),
167-
));
168-
}
169-
170-
let signature = metadata::Signature {
171-
flags: metadata::MethodCallAttributes::HASTHIS,
172-
return_type,
173-
types,
174-
};
279+
InterfaceMember::Property(prop) => {
280+
let is_get_only = prop.attrs.iter().any(|a| a.path().is_ident("get"));
281+
let is_set_only = prop.attrs.iter().any(|a| a.path().is_ident("set"));
175282

176-
let mut is_special = false;
177-
for attr in &method.attrs {
178-
if attr.path().is_ident("special") {
179-
if !matches!(attr.meta, syn::Meta::Path(_)) {
180-
return self.err(attr, "`special` attribute does not accept arguments");
283+
if is_get_only && is_set_only {
284+
return self.err(
285+
&prop.name,
286+
"property cannot have both `#[get]` and `#[set]` attributes",
287+
);
181288
}
182-
is_special = true;
183-
}
184-
}
185289

186-
let mut flags = base_flags;
187-
if is_special {
188-
flags |= metadata::MethodAttributes::SpecialName;
189-
}
290+
for attr in &prop.attrs {
291+
if !attr.path().is_ident("get") && !attr.path().is_ident("set") {
292+
return self.err(
293+
attr,
294+
"only `#[get]` and `#[set]` attributes are supported on properties",
295+
);
296+
}
297+
if !matches!(attr.meta, syn::Meta::Path(_)) {
298+
return self
299+
.err(attr, "`get`/`set` attribute does not accept arguments");
300+
}
301+
}
190302

191-
let method_def = self.output.MethodDef(
192-
&method.sig.ident.to_string(),
193-
&signature,
194-
flags,
195-
Default::default(),
196-
);
303+
let ty = self.encode_type(&prop.ty)?;
304+
let method_flags = base_flags | metadata::MethodAttributes::SpecialName;
305+
306+
if !is_set_only {
307+
let get_name = format!("get_{}", prop.name);
308+
let signature = metadata::Signature {
309+
flags: metadata::MethodCallAttributes::HASTHIS,
310+
return_type: ty.clone(),
311+
types: vec![],
312+
};
313+
if !already_has_guid {
314+
method_signatures.push((get_name.clone(), vec![], ty.clone()));
315+
}
316+
self.output.MethodDef(
317+
&get_name,
318+
&signature,
319+
method_flags,
320+
Default::default(),
321+
);
322+
self.encode_simple_params(&[])?;
323+
}
197324

198-
self.encode_attrs(
199-
metadata::writer::HasAttribute::MethodDef(method_def),
200-
&method.attrs,
201-
&["special"],
202-
)?;
325+
if !is_get_only {
326+
let put_name = format!("put_{}", prop.name);
327+
let signature = metadata::Signature {
328+
flags: metadata::MethodCallAttributes::HASTHIS,
329+
return_type: metadata::Type::Void,
330+
types: vec![ty.clone()],
331+
};
332+
if !already_has_guid {
333+
method_signatures.push((
334+
put_name.clone(),
335+
vec![ty.clone()],
336+
metadata::Type::Void,
337+
));
338+
}
339+
self.output.MethodDef(
340+
&put_name,
341+
&signature,
342+
method_flags,
343+
Default::default(),
344+
);
345+
self.encode_simple_params(&[("value", &ty)])?;
346+
}
347+
}
203348

204-
self.encode_return_attrs(&method.return_attrs)?;
205-
self.encode_params(&params)?;
349+
InterfaceMember::Event(evt) => {
350+
let handler_ty = self.encode_type(&evt.handler_ty)?;
351+
let token_ty =
352+
metadata::Type::value_named("Windows.Foundation", "EventRegistrationToken");
353+
let method_flags = base_flags | metadata::MethodAttributes::SpecialName;
354+
355+
let add_name = format!("add_{}", evt.name);
356+
let add_signature = metadata::Signature {
357+
flags: metadata::MethodCallAttributes::HASTHIS,
358+
return_type: token_ty.clone(),
359+
types: vec![handler_ty.clone()],
360+
};
361+
if !already_has_guid {
362+
method_signatures.push((
363+
add_name.clone(),
364+
vec![handler_ty.clone()],
365+
token_ty.clone(),
366+
));
367+
}
368+
self.output.MethodDef(
369+
&add_name,
370+
&add_signature,
371+
method_flags,
372+
Default::default(),
373+
);
374+
self.encode_simple_params(&[("handler", &handler_ty)])?;
375+
376+
let remove_name = format!("remove_{}", evt.name);
377+
let remove_signature = metadata::Signature {
378+
flags: metadata::MethodCallAttributes::HASTHIS,
379+
return_type: metadata::Type::Void,
380+
types: vec![token_ty.clone()],
381+
};
382+
if !already_has_guid {
383+
method_signatures.push((
384+
remove_name.clone(),
385+
vec![token_ty.clone()],
386+
metadata::Type::Void,
387+
));
388+
}
389+
self.output.MethodDef(
390+
&remove_name,
391+
&remove_signature,
392+
method_flags,
393+
Default::default(),
394+
);
395+
self.encode_simple_params(&[("token", &token_ty)])?;
396+
}
397+
}
206398
}
207399

208400
if !already_has_guid {

0 commit comments

Comments
 (0)