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