1use std::{fs::File, io::BufReader, num::NonZeroU32, path::Path};
2
3use proc_macro2::TokenStream;
4use quick_xml::{Reader, events::attributes::Attributes};
5use quote::quote;
6
7use quick_xml::events::Event as Ev;
8
9#[derive(Debug)]
10pub struct Protocol {
11 _name: String,
12 interfaces: Vec<Interface>,
13 _summary: String,
14 _description: String,
15}
16
17impl Protocol {
18 pub fn new<P: AsRef<Path>>(file: P) -> Self {
19 let mut reader = Reader::from_file(file).unwrap();
20 let mut name = None;
21 let mut interfaces = Vec::new();
22 let mut summary = "".to_string();
23 let mut description = "".to_string();
24
25 let mut buf = Vec::new();
26 loop {
27 match reader.read_event_into(&mut buf) {
28 Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
29 Ok(Ev::Eof) => break,
31 Ok(Ev::Start(e)) => match e.name().as_ref() {
32 b"protocol" => {
33 for attr in e.attributes() {
34 match attr {
35 Ok(attr) => {
36 if attr.key.0 == b"name" {
37 name = Some(
38 std::str::from_utf8(&attr.value).unwrap().to_string(),
39 );
40 }
41 }
42 Err(e) => panic!("failed to read xml: {e}"),
43 }
44 }
45 }
46
47 b"interface" => interfaces.push(Interface::new(&mut reader, e.attributes())),
48
49 b"copyright" => {
50 let _ = reader.read_event_into(&mut buf);
52 let _ = reader.read_event_into(&mut buf);
54 }
55
56 b"description" => {
57 (summary, description) = parse_description(&mut reader, e.attributes())
58 }
59 e => panic!("unrecognized tag: {:?}", std::str::from_utf8(e)),
60 },
61 Ok(Ev::Text(_)) | Ok(Ev::Comment(_)) => continue,
62 Ok(Ev::Decl(decl)) => {
63 if let Some(Ok(enc)) = decl.encoding()
64 && *enc != *b"UTF-8"
65 && *enc != *b"utf-8"
66 {
67 panic!("xml file is not encoded with utf-8");
68 }
69 }
70 Ok(Ev::End(end)) => match end.name().as_ref() {
71 b"protocol" => break,
72 b"description" => continue,
73 _ => panic!("bad xml file: {}", reader.buffer_position()),
74 },
75 e => panic!("unprocessed xml event: {e:?}"),
76 }
77 }
78
79 Self {
80 _name: name.unwrap(),
81 interfaces,
82 _summary: summary,
83 _description: description,
84 }
85 }
86
87 pub fn generate(self) -> TokenStream {
88 self.interfaces
89 .into_iter()
90 .map(Interface::generate)
91 .collect()
92 }
93}
94
95#[derive(Debug)]
96struct Interface {
97 name: String,
98 summary: String,
99 description: String,
100 version: NonZeroU32,
101 requests: Vec<Request>,
102 events: Vec<Event>,
103 enums: Vec<Enum>,
104}
105
106impl Interface {
107 fn new(reader: &mut Reader<BufReader<File>>, attrs: Attributes) -> Self {
108 let (name, version) = {
109 let mut name = "".to_string();
110 let mut version = 0;
111 for attr in attrs.flatten() {
112 match attr.key.as_ref() {
113 b"name" => name = attr.unescape_value().unwrap().to_string(),
114 b"version" => version = attr.unescape_value().unwrap().parse::<u32>().unwrap(),
115
116 e => panic!(
117 "unrecognized attribute in interface: {}",
118 String::from_utf8_lossy(e)
119 ),
120 }
121 }
122 assert!(!name.is_empty());
123 (name, NonZeroU32::new(version).unwrap())
124 };
125
126 let mut summary = "".to_string();
127 let mut description = "".to_string();
128 let mut requests = Vec::new();
129 let mut events = Vec::new();
130 let mut enums = Vec::new();
131
132 let mut buf = Vec::new();
133 loop {
134 match reader.read_event_into(&mut buf) {
135 Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
136 Ok(Ev::Start(e)) => match e.name().as_ref() {
137 b"description" => {
138 (summary, description) = parse_description(reader, e.attributes())
139 }
140 b"request" => requests.push(Request::new(reader, e.attributes())),
141 b"event" => events.push(Event::new(reader, e.attributes())),
142 b"enum" => enums.push(Enum::new(reader, e.attributes())),
143 e => panic!("unrecognized tag: {:?}", std::str::from_utf8(e)),
144 },
145 Ok(Ev::Text(_)) | Ok(Ev::Comment(_)) => continue,
146 Ok(Ev::End(end)) => match end.name().as_ref() {
147 b"description" => continue,
148 b"interface" => break,
149 _ => panic!("bad xml file: {end:?} at {}", reader.buffer_position()),
150 },
151 e => panic!("unprocessed xml event: {e:?}"),
152 }
153 }
154
155 Self {
156 name,
157 summary,
158 description,
159 version,
160 requests,
161 events,
162 enums,
163 }
164 }
165
166 fn generate(self) -> TokenStream {
167 let name = &self.name;
168 let name_ident = ident(name);
169 let mut doc = self.summary;
170 for line in self.description.lines() {
171 doc.push('\n');
172 doc += line.trim();
173 }
174 let version = self.version.get();
175
176 let events = self.events.iter().map(|ev| {
177 let mut doc = ev.summary.clone();
178 for line in ev.description.lines() {
179 doc.push('\n');
180 doc += line.trim();
181 }
182
183 if ev.destructor {
184 doc += "\nTHIS IS A DESTRUCTOR"
185 }
186
187 if let Some(d) = ev.deprecated_since {
190 doc.push_str(&format!("\nDeprecated since interface version {d}"));
191 }
192
193 let args = ev.args.iter().map(|arg| arg.gen_fn_args(false));
194
195 let fn_name = ident(&ev.name);
196 quote! {
197 #[doc = #doc]
198 fn #fn_name(&mut self, sender_id: waybackend::types::ObjectId, #(#args),*);
199 }
200 });
201
202 let requests = self.requests.iter().enumerate().map(|(i, req)| {
203 let ident = ident(&req.name.to_uppercase());
204 let mut doc = req.summary.clone();
205 if let Some(desc) = req.description.as_ref() {
206 for line in desc.lines() {
207 doc.push('\n');
208 doc += line.trim();
209 }
210 }
211
212 if req.destructor {
213 doc += "\nTHIS IS A DESTRUCTOR"
214 }
215
216 let deprecation_warning = if let Some(d) = req.deprecated_since {
217 let note = format!("deprecation since interface version {d}");
218 quote! {#[deprecated(note=#note)]}
219 } else {
220 TokenStream::new()
221 };
222
223 let i = i as u16;
224 let function = req.generate();
225 quote! {
226 pub const #ident : u16 = #i;
227 #[doc = #doc]
228 #deprecation_warning
229 #function
230 }
231 });
232
233 let events_dispatch = self
234 .events
235 .iter()
236 .enumerate()
237 .map(|(i, ev)| ev.generate(i as u16));
238 let enums = self.enums.into_iter().map(|i| i.generate());
239
240 let event = quote! {
241 pub fn event<T: EvHandler>(
242 state: &mut T,
243 wire_msg: &mut waybackend::wire::Messages<'_>,
244 ) -> Result<(), waybackend::wire::Error> {
245 match wire_msg.op() {
246 #(#events_dispatch)*
247 otherwise => Err(waybackend::wire::Error::UnrecognizedEventOpCode(#name, otherwise))
248 }
249 }
250 };
251
252 quote! {
253 #[doc = #doc]
254 #[allow(
255 non_upper_case_globals,
256 non_camel_case_types,
257 unused_imports,
258 unused,
259 clippy::too_many_arguments,
260 clippy::match_single_binding,
261 clippy::empty_docs,
262 clippy::just_underscores_and_digits
263 )]
264 pub mod #name_ident {
265 use super::*;
266
267 pub const MAX_VERSION: u32 = #version;
268 pub const NAME: &str = #name;
269
270 #[doc = "Events for this interface"]
271 pub trait EvHandler {
272 #(#events)*
273 }
274
275 #event
276
277 #[doc = "Requests for this interface"]
278 pub mod req {
279 use super::*;
280 #(#requests)*
281 }
282
283 #(#enums)*
284 }
285 }
286 }
287}
288
289fn parse_description(reader: &mut Reader<BufReader<File>>, attrs: Attributes) -> (String, String) {
291 if let Some(attr) = attrs.flatten().next() {
292 match attr.key.as_ref() {
293 b"summary" => {
294 let summary = std::str::from_utf8(&attr.value).unwrap().to_string();
295 let mut buf = Vec::new();
296 loop {
297 match reader.read_event_into(&mut buf) {
298 Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
299 Ok(Ev::Text(text)) => return (summary, text.decode().unwrap().into()),
300 Ok(Ev::Comment(_)) => continue,
301 Ok(ev) => {
302 panic!(
303 "Unexpected xml tag {:?} at: {}",
304 ev,
305 reader.buffer_position()
306 )
307 }
308 }
309 }
310 }
311 other => panic!("found unexpected attribute in description: {:?}", other),
312 }
313 }
314 unreachable!("failed to parse description's summary and description")
315}
316
317#[derive(Debug)]
318struct Request {
319 name: String,
320 summary: String,
321 destructor: bool,
322 deprecated_since: Option<u16>,
323 description: Option<String>,
324 args: Vec<Arg>,
325}
326
327impl Request {
328 fn new(reader: &mut Reader<BufReader<File>>, attrs: Attributes) -> Self {
329 let mut destructor = false;
330 let mut deprecated_since = None;
331 let name = {
332 let mut name = "".to_string();
333 for attr in attrs.flatten() {
334 match attr.key.as_ref() {
335 b"name" => name = attr.unescape_value().unwrap().to_string(),
336 b"deprecated-since" => {
337 deprecated_since = Some(attr.unescape_value().unwrap().parse().unwrap())
338 }
339 b"since" => continue, b"type" => match attr.unescape_value().unwrap().as_ref() {
341 "destructor" => destructor = true,
342 e => panic!("unrecognized request type: {e:?}"),
343 },
344 e => panic!(
345 "unrecognized attribute in interface request: {}",
346 String::from_utf8_lossy(e)
347 ),
348 }
349 }
350 assert!(!name.is_empty());
351 name
352 };
353
354 let mut summary = "".to_string();
355 let mut description = None;
356 let mut args = Vec::new();
357
358 let mut buf = Vec::new();
359 loop {
360 match reader.read_event_into(&mut buf) {
361 Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
362 Ok(Ev::Start(e)) => match e.name().as_ref() {
363 b"description" => {
364 let parsed = parse_description(reader, e.attributes());
365 summary = parsed.0;
366 description = Some(parsed.1);
367 }
368 e => panic!("unrecognized tag: {:?}", std::str::from_utf8(e)),
369 },
370 Ok(Ev::Empty(e)) => match e.name().as_ref() {
371 b"arg" => args.push(Arg::new(e.attributes())),
372 b"description" => {
373 if let Some(attr) = e.attributes().flatten().next() {
374 match attr.key.as_ref() {
375 b"summary" => {
376 summary = std::str::from_utf8(&attr.value).unwrap().to_string()
377 }
378 e => panic!("unrecognized description attribute: {e:?}"),
379 }
380 }
381 }
382 e => panic!(
383 "unrecognized tag: {:?} at: {}",
384 std::str::from_utf8(e),
385 reader.buffer_position()
386 ),
387 },
388 Ok(Ev::Text(_)) | Ok(Ev::Comment(_)) => continue,
389 Ok(Ev::End(end)) => match end.name().as_ref() {
390 b"description" => continue,
391 b"request" => break,
392 _ => panic!("bad xml file: {end:?} at {}", reader.buffer_position()),
393 },
394 e => panic!("unprocessed xml event: {e:?}"),
395 }
396 }
397
398 Self {
399 name,
400 summary,
401 description,
402 deprecated_since,
403 destructor,
404 args,
405 }
406 }
407
408 fn generate(&self) -> TokenStream {
409 let name = ident(&self.name);
410
411 let opcode = ident(&self.name.to_uppercase());
412
413 let args = self.args.iter().map(|arg| arg.gen_fn_args(true));
414 let args_builder = self.args.iter().map(|arg| arg.gen_builder());
415
416 let mut fds = 0usize;
417 let sizes = self.args.iter().filter_map(|arg| match arg.arg_type {
418 ArgType::String => {
419 let ident = ident(&arg.name);
420 Some(quote! { 1 + (#ident.len() + 1).div_ceil(4) })
421 }
422 ArgType::Array => {
423 let ident = ident(&arg.name);
424 Some(quote! { 1 + #ident.len().div_ceil(4) })
425 }
426 ArgType::NewId if arg.interface.is_none() => {
427 Some(quote! { 2 + 1 + (interface.len() + 1).div_ceil(4) })
428 }
429 ArgType::Fd => {
430 fds += 1;
431 None
432 }
433 _ => Some(quote! { 1 }),
434 });
435
436 quote! {
437 pub fn #name(
438 backend: &mut waybackend::Waybackend,
439 sender_id: waybackend::types::ObjectId,
440 #(#args),*
441 ) -> Result<(), waybackend::Errno> {
442 let waybackend::Waybackend {wire_msg_builder, wayland_fd, ..} = backend;
443 {
444 let byte_size = 2 #(+ #sizes)*;
445 wire_msg_builder.ensure_space(wayland_fd, byte_size, #fds)?;
446 unsafe { wire_msg_builder.add_header(sender_id, #opcode, (byte_size * 4) as u16) };
447 }
448 unsafe { #(#args_builder)* }
449 Ok(())
450 }
451 }
452 }
453}
454
455#[derive(Debug)]
456struct Event {
457 name: String,
458 summary: String,
459 destructor: bool,
460 deprecated_since: Option<u16>,
461 description: String,
462 args: Vec<Arg>,
463}
464
465impl Event {
466 fn new(reader: &mut Reader<BufReader<File>>, attrs: Attributes) -> Self {
467 let mut destructor = false;
468 let mut deprecated_since = None;
469 let name = {
470 let mut name = "".to_string();
471 for attr in attrs.flatten() {
472 match attr.key.as_ref() {
473 b"name" => name = attr.unescape_value().unwrap().to_string(),
474 b"deprecated-since" => {
475 deprecated_since = Some(attr.unescape_value().unwrap().parse().unwrap())
476 }
477 b"since" => continue, b"type" => match attr.unescape_value().unwrap().as_ref() {
479 "destructor" => destructor = true,
480 e => panic!("unrecognized event type: {e:?}"),
481 },
482 e => panic!(
483 "unrecognized attribute in interface event: {}",
484 String::from_utf8_lossy(e)
485 ),
486 }
487 }
488 assert!(!name.is_empty());
489 name
490 };
491
492 let mut summary = "".to_string();
493 let mut description = "".to_string();
494 let mut args = Vec::new();
495
496 let mut buf = Vec::new();
497 loop {
498 match reader.read_event_into(&mut buf) {
499 Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
500 Ok(Ev::Start(e)) => match e.name().as_ref() {
501 b"description" => {
502 (summary, description) = parse_description(reader, e.attributes())
503 }
504 e => panic!("unrecognized tag: {:?}", std::str::from_utf8(e)),
505 },
506 Ok(Ev::Empty(e)) => match e.name().as_ref() {
507 b"arg" => args.push(Arg::new(e.attributes())),
508 b"description" => {
509 if let Some(attr) = e.attributes().flatten().next() {
510 match attr.key.as_ref() {
511 b"summary" => {
512 summary = std::str::from_utf8(&attr.value).unwrap().to_string()
513 }
514 e => panic!("unrecognized description attribute: {e:?}"),
515 }
516 }
517 }
518 e => panic!("unrecognized tag: {:?}", std::str::from_utf8(e)),
519 },
520 Ok(Ev::Text(_)) | Ok(Ev::Comment(_)) => continue,
521 Ok(Ev::End(end)) => match end.name().as_ref() {
522 b"description" => continue,
523 b"event" => break,
524 _ => panic!("bad xml file: {end:?} at {}", reader.buffer_position()),
525 },
526 e => panic!("unprocessed xml event: {e:?}"),
527 }
528 }
529
530 Self {
531 name,
532 summary,
533 description,
534 deprecated_since,
535 destructor,
536 args,
537 }
538 }
539
540 fn generate(&self, opcode: u16) -> TokenStream {
541 let arg_idents = self.args.iter().map(|arg| ident(&arg.name));
542 let args = self.args.iter().map(|arg| arg.gen_recv());
543
544 let function = ident(&self.name);
545
546 if self.args.iter().any(|arg| {
547 matches!(
548 arg.arg_type,
549 ArgType::String | ArgType::Array | ArgType::NewId if arg.interface.is_none()
550 )
551 }) {
552 let boolean_array = self.args.iter().filter_map(|arg| match arg.arg_type {
555 ArgType::String | ArgType::Array => Some(quote!(true)),
556 ArgType::NewId if arg.interface.is_none() => Some(quote!(false, true, false)),
557 ArgType::Fd => None,
558 _ => Some(quote!(false)),
559 });
560 quote! {
561 #opcode => {
562 wire_msg.validate_len([#(#boolean_array),*])?;
563 #(#args)*
565
566 state.#function(wire_msg.sender_id(), #(#arg_idents),*);
568 Ok(())
569 },
570 }
571 } else {
572 quote! {
573 #opcode => {
574 #(#args)*
576
577 state.#function(wire_msg.sender_id(), #(#arg_idents),*);
579 Ok(())
580 },
581 }
582 }
583 }
584}
585
586#[derive(Debug)]
587struct EnumEntry {
588 name: String,
589 summary: Option<String>,
590 description: Option<String>,
591 value: u32,
592}
593
594impl EnumEntry {
595 fn new(reader: &mut Reader<BufReader<File>>, attrs: Attributes) -> Self {
596 let mut name = "".to_string();
597 let mut summary = None;
598 let mut description = None;
599 let mut value = None;
600
601 for attr in attrs.flatten() {
602 match attr.key.as_ref() {
603 b"name" => name = name_to_pascal_case(&attr.unescape_value().unwrap()),
604 b"value" => {
605 let s = attr.unescape_value().unwrap();
606 value = if let Some(s) = s.as_ref().strip_prefix("0x") {
607 Some(u32::from_str_radix(s, 16).unwrap())
608 } else {
609 Some(s.as_ref().parse::<u32>().unwrap())
610 };
611 }
612 b"since" => continue, b"summary" => summary = Some(attr.unescape_value().unwrap().to_string()),
614 e => panic!(
615 "unrecognized attribute in interface: {}",
616 std::str::from_utf8(e).unwrap()
617 ),
618 }
619 }
620 let mut buf = Vec::new();
621 loop {
622 match reader.read_event_into(&mut buf) {
623 Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
624 Ok(Ev::Start(e)) => match e.name().as_ref() {
625 b"description" => {
626 let parsed = parse_description(reader, e.attributes());
627 summary = Some(parsed.0);
628 description = Some(parsed.1);
629 }
630 e => panic!("unrecognized tag: {:?}", std::str::from_utf8(e)),
631 },
632 Ok(Ev::Text(_)) | Ok(Ev::Comment(_)) => continue,
633 Ok(Ev::End(end)) => match end.name().as_ref() {
634 b"entry" => break,
635 b"description" => continue,
636 _ => panic!("bad xml file: {end:?} at {}", reader.buffer_position()),
637 },
638 e => panic!("unprocessed xml event: {e:?}"),
639 }
640 }
641
642 assert!(!name.is_empty());
643 assert!(value.is_some());
644
645 Self {
646 name,
647 summary,
648 description,
649 value: value.unwrap(),
650 }
651 }
652 fn new_from_empty_tag(attrs: Attributes) -> Self {
653 let mut name = "".to_string();
654 let mut summary = None;
655 let mut value = None;
656
657 for attr in attrs.flatten() {
658 match attr.key.as_ref() {
659 b"name" => name = attr.unescape_value().unwrap().to_string(),
660 b"value" => {
661 let s = attr.unescape_value().unwrap();
662 value = if let Some(s) = s.as_ref().strip_prefix("0x") {
663 Some(u32::from_str_radix(s, 16).unwrap())
664 } else {
665 Some(s.as_ref().parse::<u32>().unwrap())
666 };
667 }
668 b"since" => continue, b"summary" => summary = Some(attr.unescape_value().unwrap().to_string()),
670 e => panic!(
671 "unrecognized attribute in interface: {}",
672 std::str::from_utf8(e).unwrap()
673 ),
674 }
675 }
676
677 assert!(!name.is_empty());
678 assert!(value.is_some());
679
680 Self {
681 name,
682 summary,
683 description: None,
684 value: value.unwrap(),
685 }
686 }
687
688 fn generate(&self) -> TokenStream {
689 let name = ident(&self.name);
690
691 let mut doc = self.summary.as_ref().cloned().unwrap_or_default();
692 if let Some(desc) = self.description.as_ref() {
693 for line in desc.lines() {
694 doc.push('\n');
695 doc += line.trim();
696 }
697 }
698
699 let value = self.value;
700
701 quote! {
702 #[doc = #doc]
703 #name = #value
704 }
705 }
706
707 fn generate_as_consts(&self) -> TokenStream {
708 let name = ident(&self.name);
709
710 let mut doc = self.summary.as_ref().cloned().unwrap_or_default();
711 if let Some(desc) = self.description.as_ref() {
712 for line in desc.lines() {
713 doc.push('\n');
714 doc += line.trim();
715 }
716 }
717
718 let value = self.value;
719
720 quote! {
721 #[doc = #doc]
722 const #name = #value
723 }
724 }
725}
726
727#[derive(Debug)]
728struct Enum {
729 name: String,
730 description: Option<String>,
731 summary: Option<String>,
732 is_bitfield: bool,
733 variants: Vec<EnumEntry>,
734}
735
736impl Enum {
737 fn new(reader: &mut Reader<BufReader<File>>, attrs: Attributes) -> Self {
738 let mut is_bitfield = false;
739
740 let name = {
741 let mut name = "".to_string();
742 for attr in attrs.flatten() {
743 match attr.key.as_ref() {
744 b"name" => name = name_to_pascal_case(&attr.unescape_value().unwrap()),
745 b"since" => continue, b"bitfield" => is_bitfield = attr.unescape_value().unwrap().as_ref().eq("true"),
747 e => panic!("unrecognized attribute in interface: {e:?}"),
748 }
749 }
750 assert!(!name.is_empty());
751 name
752 };
753
754 let mut summary = None;
755 let mut description = None;
756 let mut variants = Vec::new();
757
758 let mut buf = Vec::new();
759 loop {
760 match reader.read_event_into(&mut buf) {
761 Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
762 Ok(Ev::Start(e)) => match e.name().as_ref() {
763 b"description" => {
764 let parsed = parse_description(reader, e.attributes());
765 summary = Some(parsed.0);
766 description = Some(parsed.1);
767 }
768 b"entry" => variants.push(EnumEntry::new(reader, e.attributes())),
769 e => panic!("unrecognized tag: {:?}", std::str::from_utf8(e)),
770 },
771 Ok(Ev::Empty(e)) => match e.name().as_ref() {
772 b"entry" => variants.push(EnumEntry::new_from_empty_tag(e.attributes())),
773 e => panic!("unrecognized tag: {:?}", std::str::from_utf8(e)),
774 },
775 Ok(Ev::Text(_)) | Ok(Ev::Comment(_)) => continue,
776 Ok(Ev::End(end)) => match end.name().as_ref() {
777 b"description" => continue,
778 b"enum" => break,
779 _ => panic!("bad xml file: {end:?} at {}", reader.buffer_position()),
780 },
781 e => panic!("unprocessed xml event: {e:?}"),
782 }
783 }
784
785 if is_bitfield {
786 for var in variants.iter_mut() {
787 var.name = var.name.to_uppercase();
788 }
789 }
790
791 Self {
792 name,
793 is_bitfield,
794 summary,
795 description,
796 variants,
797 }
798 }
799
800 fn generate(self) -> TokenStream {
801 let name = ident(&self.name);
802 let name_str = &self.name;
803 let mut doc = String::new();
804 doc.push_str(&self.summary.unwrap_or_default());
805 if let Some(desc) = self.description {
806 for line in desc.lines() {
807 doc.push('\n');
808 doc += line.trim();
809 }
810 }
811 if self.is_bitfield {
812 let variants = self.variants.iter().map(|v| v.generate_as_consts());
813 quote! {
814 waybackend::bitflags::bitflags! {
815 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
816 #[doc = #doc]
817 pub struct #name : u32 {
818 #(#variants ;)*
819 const _ = !0;
820 }
821 }
822
823 impl core::convert::TryFrom<u32> for #name {
824 type Error = waybackend::wire::Error;
825 fn try_from(value: u32) -> Result<Self, Self::Error> {
826 Ok(Self::from_bits_retain(value))
827 }
828 }
829
830 impl core::convert::From<#name> for u32 {
831 fn from(value: #name) -> u32 {
832 value.bits()
833 }
834 }
835 }
836 } else {
837 let variants = self.variants.iter().map(|v| v.generate());
838 let variants2 = self.variants.iter().map(|v| ident(&v.name));
839 let variants3 = self.variants.iter().map(|v| ident(&v.name));
840 quote! {
841 #[doc = #doc]
842 #[repr(u32)]
843 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
844 pub enum #name {
845 #(#variants,)*
846 }
847
848 impl core::convert::TryFrom<u32> for #name {
849 type Error = waybackend::wire::Error;
850
851 #[allow(non_upper_case_globals)]
852 fn try_from(value: u32) -> Result<Self, Self::Error> {
853 #(const #variants2: u32 = #name::#variants2 as u32;)*
854 match value {
855 #(#variants3 => Ok(Self::#variants3),)*
856 otherwise => Err(waybackend::wire::Error::InvalidEnumDiscriminant(#name_str, otherwise)),
857 }
858 }
859 }
860
861 impl core::convert::From<#name> for u32 {
862 fn from(value: #name) -> u32 {
863 value as u32
864 }
865 }
866 }
867 }
868 }
869}
870
871#[derive(Debug)]
872struct Arg {
873 name: String,
874 arg_type: ArgType,
875 #[allow(unused)]
876 summary: String,
877 interface: Option<String>,
878 r#enum: Option<String>,
879 allow_null: bool,
880}
881
882#[derive(Debug, PartialEq, Eq)]
883enum ArgType {
884 Uint,
885 Int,
886 Fixed,
887 String,
888 Object,
889 NewId,
890 Array,
891 Fd,
892}
893
894impl ArgType {
895 fn from_raw(raw: &str) -> Self {
896 match raw {
897 "uint" => Self::Uint,
898 "int" => Self::Int,
899 "fixed" => Self::Fixed,
900 "string" => Self::String,
901 "object" => Self::Object,
902 "new_id" => Self::NewId,
903 "array" => Self::Array,
904 "fd" => Self::Fd,
905 e => panic!("unrecognized type: {e}"),
906 }
907 }
908}
909
910impl Arg {
911 fn new(attrs: Attributes) -> Self {
912 let mut name = "".to_string();
913 let mut summary = "".to_string();
914 let mut arg_type = None;
915 let mut interface = None;
916 let mut r#enum = None;
917 let mut allow_null = false;
918 for attr in attrs.flatten() {
919 match attr.key.as_ref() {
920 b"name" => name = attr.unescape_value().unwrap().to_string(),
921 b"summary" => summary = attr.unescape_value().unwrap().to_string(),
922 b"type" => arg_type = Some(ArgType::from_raw(&attr.unescape_value().unwrap())),
923 b"interface" => interface = Some(attr.unescape_value().unwrap().to_string()),
924 b"enum" => r#enum = Some(attr.unescape_value().unwrap().to_string()),
925 b"allow-null" => allow_null = attr.unescape_value().unwrap().eq("true"),
926 e => panic!(
927 "unrecognized attribute in interface: {}",
928 std::str::from_utf8(e).unwrap()
929 ),
930 }
931 }
932
933 assert!(!name.is_empty());
934 assert!(arg_type.is_some());
935
936 Self {
937 name,
938 arg_type: arg_type.unwrap(),
939 summary,
940 interface,
941 r#enum,
942 allow_null,
943 }
944 }
945
946 fn gen_enum_type(&self, e: &str) -> TokenStream {
947 let mut t = TokenStream::new();
948 let mut i = 0;
949 while let Some(j) = e[i..].find(".") {
950 let id = ident(&e[i..i + j]);
951 t = if t.is_empty() {
952 quote! {#id}
953 } else {
954 quote! {#t :: #id}
955 };
956 i += j + 1;
957 }
958 let id = ident(&name_to_pascal_case(&e[i..]));
959 if t.is_empty() {
960 quote! {#id}
961 } else {
962 quote! {#t :: #id}
963 }
964 }
965
966 fn gen_recv(&self) -> TokenStream {
967 let name = ident(&self.name);
968
969 if let Some(e) = &self.r#enum {
970 let enum_ident = self.gen_enum_type(e);
971 return quote! { let #name: #enum_ident = wire_msg.next_u32().try_into()?; };
972 }
973
974 match self.arg_type {
975 ArgType::Uint => quote! { let #name = wire_msg.next_u32(); },
976 ArgType::Int => quote! { let #name = wire_msg.next_i32(); },
977 ArgType::Fixed => quote! { let #name = wire_msg.next_fixed(); },
978 ArgType::String => quote! { let #name = wire_msg.next_string()?; },
979 ArgType::Object => {
980 if self.allow_null {
981 quote! { let #name = wire_msg.next_object(); }
982 } else {
983 quote! { let #name = wire_msg.next_object().ok_or(waybackend::wire::Error::NullObjectId)?; }
984 }
985 }
986 ArgType::NewId => {
987 if self.interface.is_some() {
988 quote! { let #name = wire_msg.next_new_specified_id()?; }
989 } else {
990 quote! { let #name = wire_msg.next_new_unspecified_id()?; }
991 }
992 }
993 ArgType::Array => quote! { let #name = wire_msg.next_array(); },
994 ArgType::Fd => quote! { let #name = wire_msg.next_fd(); },
995 }
996 }
997
998 fn gen_fn_args(&self, is_request: bool) -> TokenStream {
999 let name = ident(&self.name);
1000
1001 if let Some(e) = &self.r#enum {
1002 let enum_ident = self.gen_enum_type(e);
1003 return quote! { #name: #enum_ident };
1004 }
1005
1006 match self.arg_type {
1007 ArgType::Uint => quote! { #name: u32 },
1008 ArgType::Int => quote! { #name: i32 },
1009 ArgType::Fixed => quote! { #name: waybackend::types::WlFixed },
1010 ArgType::String => quote! { #name: &str },
1011 ArgType::Object => {
1012 if self.allow_null {
1013 quote! { #name: Option<waybackend::types::ObjectId> }
1014 } else {
1015 quote! { #name: waybackend::types::ObjectId }
1016 }
1017 }
1018 ArgType::NewId => {
1019 if self.interface.is_some() {
1020 quote! { #name: waybackend::types::ObjectId }
1021 } else {
1022 quote! { #name: waybackend::types::ObjectId, interface: &str, version: u32 }
1023 }
1024 }
1025 ArgType::Array => quote! { #name: &[u8] },
1026 ArgType::Fd => {
1027 if is_request {
1028 quote! { #name: &impl waybackend::rustix::fd::AsRawFd }
1029 } else {
1030 quote! { #name: waybackend::rustix::fd::OwnedFd }
1031 }
1032 }
1033 }
1034 }
1035
1036 fn gen_builder(&self) -> TokenStream {
1037 let name = ident(&self.name);
1038
1039 if self.r#enum.is_some() {
1040 return quote! { wire_msg_builder.add_u32(#name.into()); };
1041 }
1042
1043 match self.arg_type {
1044 ArgType::Uint => quote! { wire_msg_builder.add_u32(#name); },
1045 ArgType::Int => quote! { wire_msg_builder.add_i32(#name); },
1046 ArgType::Fixed => quote! { wire_msg_builder.add_fixed(#name); },
1047 ArgType::String => quote! { wire_msg_builder.add_string(#name); },
1048 ArgType::Object => {
1049 if self.allow_null {
1050 quote! { wire_msg_builder.add_object(#name); }
1051 } else {
1052 quote! { wire_msg_builder.add_object(Some(#name)); }
1053 }
1054 }
1055 ArgType::NewId => {
1056 if self.interface.is_some() {
1057 quote! { wire_msg_builder.add_new_specified_id(#name); }
1058 } else {
1059 quote! { wire_msg_builder.add_new_unspecified_id(#name, interface, version); }
1060 }
1061 }
1062 ArgType::Array => quote! { wire_msg_builder.add_array(#name); },
1063 ArgType::Fd => quote! { wire_msg_builder.add_fd(#name)?; },
1064 }
1065 }
1066}
1067
1068fn ident(name: &str) -> proc_macro2::Ident {
1069 if name == "state" {
1070 return proc_macro2::Ident::new("_state", proc_macro2::Span::mixed_site());
1071 }
1072 syn::parse_str(name).unwrap_or_else(|_| syn::parse_str(&format!("_{name}")).unwrap())
1073}
1074
1075fn name_to_pascal_case(name: &str) -> String {
1076 let mut s = String::new();
1077 let mut capitalize = true;
1078 for ch in name.chars() {
1079 if capitalize {
1080 capitalize = false;
1081 s.extend(ch.to_uppercase());
1082 } else if ch == '_' || ch == '-' {
1083 capitalize = true;
1084 } else {
1085 s.push(ch);
1086 }
1087 }
1088 s
1089}
1090
1091#[cfg(feature = "build-script")]
1092pub enum WaylandProtocol {
1094 Client,
1098 System(std::path::PathBuf),
1105 Local(std::path::PathBuf),
1108}
1109
1110#[cfg(feature = "build-script")]
1111pub fn build_script_generate(protocols: &[WaylandProtocol], out_file: &std::fs::File) {
1118 use std::io::{BufWriter, Write};
1119 let mut writer = BufWriter::new(out_file);
1120
1121 let wayland_protocols = pkg_config::get_variable("wayland-protocols", "pkgdatadir")
1122 .expect("failed to find wayland-protocols directory");
1123 let wayland_protocols = std::path::PathBuf::from(&wayland_protocols);
1124 let cwd = std::env::current_dir().expect("failed to get current working directory");
1125
1126 for protocol in protocols {
1127 let path = match protocol {
1128 WaylandProtocol::Client => 'brk: {
1129 if let Ok(wayland_client) = pkg_config::get_variable("wayland-client", "pkgdatadir")
1133 {
1134 let mut path = std::path::PathBuf::from(&wayland_client);
1135 path.push("wayland.xml");
1136 if path.is_file() {
1137 break 'brk path;
1138 }
1139 }
1140
1141 if let Ok(wayland_scanner) =
1142 pkg_config::get_variable("wayland-scanner", "pkgdatadir")
1143 {
1144 let mut path = std::path::PathBuf::from(&wayland_scanner);
1145 path.push("wayland.xml");
1146 if path.is_file() {
1147 break 'brk path;
1148 }
1149 }
1150
1151 panic!("could not find wayland.xml file");
1152 }
1153 WaylandProtocol::System(path) => wayland_protocols.join(path),
1154 WaylandProtocol::Local(path) => cwd.join(path),
1155 };
1156
1157 if !path.is_file() {
1158 panic!("could not find wayland protocol file {}", path.display());
1159 }
1160
1161 let code = Protocol::new(&path).generate();
1162 println!("cargo::rerun-if-changed={}", path.display());
1163 writeln!(writer, "{code}").unwrap();
1164 }
1165}