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