wayland_scanner_lib/
codegen.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::borrow::Cow;
6use std::fmt::{Display, Formatter, Result as FmtResult};
7use std::io;
8use std::str::FromStr;
9
10use crate::ast;
11use crate::parser::ArgKind;
12
13pub type IoResult = io::Result<()>;
14
15#[derive(Debug)]
16pub enum CodegenTarget {
17    Client,
18    Server,
19}
20
21#[derive(Debug)]
22pub enum ParseCodegenTargetError {
23    TargetNotFound,
24}
25
26impl Display for ParseCodegenTargetError {
27    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
28        write!(f, "{}", "TargetNotFound")
29    }
30}
31
32impl FromStr for CodegenTarget {
33    type Err = ParseCodegenTargetError;
34
35    fn from_str(s: &str) -> Result<Self, Self::Err> {
36        match s {
37            "client" => Ok(CodegenTarget::Client),
38            "server" => Ok(CodegenTarget::Server),
39            _ => Err(ParseCodegenTargetError::TargetNotFound),
40        }
41    }
42}
43
44pub struct Codegen<W: io::Write> {
45    w: W,
46}
47
48impl<W: io::Write> Codegen<W> {
49    pub fn new(w: W) -> Codegen<W> {
50        Codegen { w }
51    }
52
53    pub fn codegen(
54        &mut self,
55        target: CodegenTarget,
56        protocol: ast::Protocol,
57        dependencies: &[String],
58    ) -> IoResult {
59        self.codegen_protocol(target, protocol, dependencies)
60    }
61
62    fn codegen_protocol(
63        &mut self,
64        target: CodegenTarget,
65        protocol: ast::Protocol,
66        dependencies: &[String],
67    ) -> IoResult {
68        writeln!(self.w, "// GENERATED FILE -- DO NOT EDIT")?;
69        if let Some(ref c) = protocol.copyright {
70            writeln!(self.w, "//")?;
71            for line in c.trim().lines() {
72                writeln!(self.w, "// {}", line.trim())?;
73            }
74        }
75        writeln!(
76            self.w,
77            "
78#![allow(warnings)]
79#![allow(clippy::all)]
80use anyhow;
81#[allow(unused_imports)]
82use fuchsia_wayland_core::{{Array, Enum, Fixed, NewId, NewObject}};
83use fuchsia_wayland_core::{{ArgKind, Arg, FromArgs, IntoMessage, Message,
84                            MessageGroupSpec, MessageHeader, MessageSpec, MessageType,
85                            ObjectId, EncodeError, DecodeError, Interface}};",
86        )?;
87        for dep in dependencies.iter() {
88            writeln!(
89                self.w,
90                "
91#[allow(unused_imports)]
92use {dep}::*;",
93            )?;
94        }
95
96        for interface in protocol.interfaces.into_iter() {
97            // Most symbols will be defined in a nested module, but re-export
98            // some into the top-level namespace.
99            //
100            // Ex, for wl_display:
101            //
102            // pub mod wl_display {
103            //      pub enum Request { ... }
104            //      pub enum Event { ... }
105            //      pub struct WlDisplay;
106            // }
107            //
108            // pub use wl_display::WlDisplay;
109            // pub use wl_display::Request as WlDisplayRequest;
110            // pub use wl_display::Event as WlDisplayEvent;
111            writeln!(self.w, "pub mod {} {{", interface.name)?;
112            writeln!(self.w, "use super::*;")?;
113            self.codegen_interface_trait(&target, &interface)?;
114            self.codegen_message_enums(&target, &interface)?;
115            self.codegen_message_traits(&target, &interface)?;
116            self.codegen_enum_types(&interface)?;
117            writeln!(self.w, "}} // mod {}", interface.name)?;
118            writeln!(self.w, "")?;
119            writeln!(self.w, "pub use crate::{}::{};", interface.name, interface.rust_name())?;
120            writeln!(
121                self.w,
122                "pub use crate::{}::Request as {}Request;",
123                interface.name,
124                interface.rust_name()
125            )?;
126            writeln!(
127                self.w,
128                "pub use crate::{}::Event as {}Event;",
129                interface.name,
130                interface.rust_name()
131            )?;
132        }
133        Ok(())
134    }
135
136    /// Emits a pair of enums that describes the set of messages for a single
137    /// interface.  Each interface will have a message enum for both Requests
138    /// and Events.
139    ///
140    /// Ex:
141    ///  pub enum MyInterfaceRequest {
142    ///    Request1 { arg1: u32 },
143    ///    Request2 { name: String},
144    ///  }
145    ///  pub enum MyInterfaceEvent {
146    ///    Event1 { value: Fixed },
147    ///    Event2 { new_obj: NewId},
148    ///  }
149    fn codegen_message_enums(
150        &mut self,
151        target: &CodegenTarget,
152        interface: &ast::Interface,
153    ) -> IoResult {
154        let enum_descriptions: [(&str, &Vec<ast::Message>, ArgFormatterFn); 2] = match target {
155            CodegenTarget::Client => [
156                ("Request", &interface.requests, format_wire_arg_rust),
157                ("Event", &interface.events, format_dispatch_arg_rust),
158            ],
159            CodegenTarget::Server => [
160                ("Request", &interface.requests, format_dispatch_arg_rust),
161                ("Event", &interface.events, format_wire_arg_rust),
162            ],
163        };
164        for (name, messages, arg_formatter) in enum_descriptions {
165            writeln!(self.w, "#[derive(Debug)]")?;
166            writeln!(self.w, "pub enum {name} {{")?;
167            for message in messages.iter() {
168                if let Some(ref d) = message.description {
169                    self.codegen_description(d, "    ")?;
170                }
171                if message.args.is_empty() {
172                    // For messages without args, emit a marker enum variant.
173                    //  Ex:
174                    //      Request::Message,
175                    writeln!(self.w, "    {},", message.rust_name())?;
176                } else {
177                    // For messages with args, emit a struct enum variant with an
178                    // entry for each arg:
179                    //  Ex:
180                    //      Request::Message {
181                    //          arg1: u32,
182                    //          arg2: String,
183                    //      },
184                    writeln!(self.w, "    {} {{", message.rust_name())?;
185                    for arg in message.args.iter() {
186                        if let Some(ref summary) = arg.summary {
187                            for line in summary.lines() {
188                                writeln!(self.w, "        /// {}", line.trim())?;
189                            }
190                        }
191                        writeln!(
192                            self.w,
193                            "        {arg_name}: {arg_type},",
194                            arg_name = arg.rust_name(),
195                            arg_type = arg_formatter(&arg)
196                        )?;
197                    }
198                    writeln!(self.w, "    }},")?;
199                }
200            }
201            writeln!(self.w, "}}")?;
202            writeln!(self.w, "")?;
203
204            writeln!(self.w, "impl MessageType for {} {{", name)?;
205
206            // Generate the log method:
207            //
208            // fn log(&self, this: ObjectId) -> String {
209            //     let mut string = String::new();
210            //     match *self {
211            //         WlInterface::Message { ref arg } =>
212            //             format!("wl_interface@{}::message(arg: {:?})", this, arg),
213            //         ...
214            //     }
215            // }
216            writeln!(self.w, "    fn log(&self, this: ObjectId) -> String {{")?;
217            writeln!(self.w, "        match *self {{")?;
218            for message in messages.iter() {
219                writeln!(self.w, "            {}::{} {{", name, message.rust_name())?;
220                for arg in message.args.iter() {
221                    writeln!(self.w, "                ref {},", arg.rust_name())?;
222                }
223                writeln!(self.w, "            }} => {{")?;
224
225                // We're using format strings to build a format string, so this is
226                // a little confusing. |message_args| are the set of strings that
227                // will be joined to form the format string literal. |format_args|
228                // are the rust expressions that will be used by the format string.
229                //
230                // Anytime we put a '{{}}' into |message_args|, we'll need a
231                // corresponding expression pushed to |format_args|.
232                //
233                // We'll end up with something like:
234                //      format!("some_interface@3::message1(arg1: {}, arg2: {})", arg1, arg2)
235                write!(
236                    self.w,
237                    "                format!(\"{}@{{:?}}::{}(",
238                    interface.name, message.name
239                )?;
240                let mut message_args = vec![];
241                let mut format_args: Vec<Cow<'_, str>> = vec!["this".into()];
242                for arg in message.args.iter() {
243                    match arg.kind {
244                        ArgKind::Array => {
245                            message_args.push(format!("{}: Array[{{}}]", arg.name));
246                            format_args.push(format!("{}.len()", arg.rust_name()).into());
247                        }
248                        ArgKind::Fd => {
249                            message_args.push(format!("{}: <handle>", arg.name));
250                        }
251                        _ => {
252                            message_args.push(format!("{}: {{:?}}", arg.name));
253                            format_args.push(arg.rust_name().into());
254                        }
255                    }
256                }
257                writeln!(self.w, "{})\", {})", message_args.join(", "), format_args.join(", "))?;
258                writeln!(self.w, "            }}")?;
259            }
260            writeln!(self.w, "        }}")?;
261            writeln!(self.w, "    }}")?;
262
263            writeln!(self.w, "    fn message_name(&self) -> &'static std::ffi::CStr{{")?;
264            writeln!(self.w, "        match *self {{")?;
265            for message in messages.iter() {
266                writeln!(
267                    self.w,
268                    "            {}::{} {{ .. }} => c\"{}::{}\",",
269                    name,
270                    message.rust_name(),
271                    interface.name,
272                    message.name
273                )?;
274            }
275            writeln!(self.w, "        }}")?;
276            writeln!(self.w, "    }}")?;
277
278            writeln!(self.w, "}}")?;
279        }
280        Ok(())
281    }
282
283    fn codegen_message_traits(
284        &mut self,
285        target: &CodegenTarget,
286        interface: &ast::Interface,
287    ) -> IoResult {
288        let (into_message_name, from_args_name, into_message_messages, from_args_messages) =
289            match target {
290                CodegenTarget::Client => {
291                    ("Request", "Event", &interface.requests, &interface.events)
292                }
293                CodegenTarget::Server => {
294                    ("Event", "Request", &interface.events, &interface.requests)
295                }
296            };
297
298        self.codegen_into_message(into_message_name, into_message_messages)?;
299        self.codegen_from_args(from_args_name, from_args_messages)?;
300        Ok(())
301    }
302
303    /// Generates impls for the IntoMessage trait for a type of messages.
304    /// For clients, the type used is Request.  For servers, it is Event.
305    /// This will be the code that allows the message type to be serialized into
306    /// a Message that can be sent over channel.
307    ///
308    /// Ex:
309    ///   impl IntoMessage for Request {
310    ///       fn into_message(self, id: u32) -> Result<Message, <Self as IntoMessage>::Error> {
311    ///           let mut header = MessageHeader {...};
312    ///           let mut message = Message::new();
313    ///           message.write_header(&header);
314    ///           match self {
315    ///           MyInterfaceRequest::Request1 { uint_arg } => {
316    ///               message.write_arg(Arg::Uint(uint_arg))?;
317    ///               header.opcode = 0;
318    ///           },
319    ///           // ... Encode other requests...
320    ///           }
321    ///
322    ///           // Rewrite header with proper ordinal & length.
323    ///           header.length = msg.bytes().len() as u16;
324    ///           message.rewind();
325    ///           message.write_header(&header);
326    ///       }
327    ///   }
328    fn codegen_into_message(&mut self, name: &str, messages: &Vec<ast::Message>) -> IoResult {
329        write!(
330            self.w,
331            "\
332impl IntoMessage for {name} {{
333    type Error = EncodeError;
334    fn into_message(self, id: u32) -> Result<Message, <Self as IntoMessage>::Error> {{
335        let mut header = MessageHeader {{
336            sender: id,
337            opcode: 0,
338            length: 0,
339        }};
340        let mut msg = Message::new();
341        msg.write_header(&header)?;
342        match self {{"
343        )?;
344
345        for (op, message) in messages.iter().enumerate() {
346            write!(
347                self.w,
348                "
349        {name}::{message_name} {{\n",
350                message_name = to_camel_case(&message.name)
351            )?;
352            for arg in message.args.iter() {
353                write!(self.w, "            {arg_name},\n", arg_name = arg.rust_name())?;
354            }
355            write!(self.w, "        }} => {{\n")?;
356            for arg in message.args.iter() {
357                write!(
358                    self.w,
359                    "            msg.write_arg({arg})?;\n",
360                    arg = format_wire_arg(&arg, &arg.rust_name())
361                )?;
362            }
363            write!(
364                self.w,
365                "            header.opcode = {opcode};
366        }},",
367                opcode = op
368            )?;
369        }
370        write!(
371            self.w,
372            "
373        }}
374        header.length = msg.bytes().len() as u16;
375        msg.rewind();
376        msg.write_header(&header)?;
377        Ok(msg)
378    }}
379}}\n"
380        )
381    }
382
383    /// Generates impls for the FromArgs trait for a type of messages.
384    /// For clients, the type used is Event.  For servers, it is Request.
385    /// This will be the code that allows the message type to be deserialized
386    /// from a Message received from a channel.
387    ///
388    /// Ex:
389    ///   impl FromArgs for Request {
390    ///       fn from_args(op: u16, mut args: Vec<Arg>) -> Result<Self, anyhow::Error> {
391    ///           match op {
392    ///           0 /* Sync */ => {
393    ///             // ... Parse Sync Event...
394    ///           }
395    ///           // ... Encode other events...
396    ///           }
397    ///       }
398    ///   }
399    fn codegen_from_args(&mut self, name: &str, messages: &Vec<ast::Message>) -> IoResult {
400        write!(
401            self.w,
402            "\
403impl FromArgs for {name} {{
404    fn from_args(op: u16, mut args: Vec<Arg>) -> Result<Self, anyhow::Error> {{
405        match op {{",
406        )?;
407
408        for (op, message) in messages.iter().enumerate() {
409            write!(
410                self.w,
411                "
412        {opcode} /* {op_name} */ => {{
413            let mut iter = args.into_iter();
414            Ok({name}::{message_name} {{\n",
415                opcode = op,
416                op_name = message.name,
417                message_name = to_camel_case(&message.name)
418            )?;
419            for arg in message.args.iter() {
420                writeln!(
421                    self.w,
422                    "                {}: iter.next()
423                                             .ok_or(DecodeError::InsufficientArgs)?
424                                             .{},",
425                    arg.rust_name(),
426                    arg_to_primitive(&arg)
427                )?;
428            }
429            write!(
430                self.w,
431                "
432            }})
433        }},"
434            )?;
435        }
436        write!(
437            self.w,
438            "
439        _ => {{
440            Err(DecodeError::InvalidOpcode(op).into())
441        }},
442        }}
443    }}
444}}\n"
445        )
446    }
447
448    /// Generates a trait for each interface.
449    ///
450    /// Ex:
451    ///   pub struct MyInterface;
452    ///
453    ///   impl Interface for MyInterface {
454    ///       const NAME: &'static str = "my_interface";
455    ///       const VERSION: u32 = 0;
456    ///       type Request = MyInterfaceRequest;
457    ///       type Event = MyInterfaceEvent;
458    ///   }
459    fn codegen_interface_trait(
460        &mut self,
461        target: &CodegenTarget,
462        interface: &ast::Interface,
463    ) -> IoResult {
464        let interface_name = to_camel_case(&interface.name);
465        if let Some(ref d) = interface.description {
466            self.codegen_description(d, "")?;
467        }
468        let (incoming_type, outgoing_type) = match target {
469            CodegenTarget::Client => ("Event", "Request"),
470            CodegenTarget::Server => ("Request", "Event"),
471        };
472        writeln!(self.w, "#[derive(Debug)]")?;
473        writeln!(self.w, "pub struct {};", interface_name)?;
474        writeln!(self.w, "")?;
475        writeln!(self.w, "impl Interface for {} {{", interface_name)?;
476        writeln!(self.w, "    const NAME: &'static str = \"{}\";", interface.name)?;
477        writeln!(self.w, "    const VERSION: u32 = {};", interface.version)?;
478        write!(self.w, "    const REQUESTS: MessageGroupSpec = ")?;
479        self.codegen_message_group_spec(&interface.requests)?;
480        write!(self.w, "    const EVENTS: MessageGroupSpec = ")?;
481        self.codegen_message_group_spec(&interface.events)?;
482        writeln!(self.w, "    type Incoming = {incoming_type};")?;
483        writeln!(self.w, "    type Outgoing = {outgoing_type};")?;
484        writeln!(self.w, "}}")?;
485        writeln!(self.w, "")?;
486        Ok(())
487    }
488
489    fn codegen_message_group_spec(&mut self, messages: &Vec<ast::Message>) -> IoResult {
490        writeln!(self.w, "MessageGroupSpec(&[")?;
491        for m in messages.iter() {
492            writeln!(self.w, "        // {}", m.name)?;
493            writeln!(self.w, "        MessageSpec(&[")?;
494            for arg in m.args.iter() {
495                writeln!(self.w, "            {},", format_arg_kind(&arg))?;
496            }
497            writeln!(self.w, "        ]),")?;
498        }
499        writeln!(self.w, "    ]);")?;
500        Ok(())
501    }
502
503    fn codegen_enum_types(&mut self, interface: &ast::Interface) -> IoResult {
504        for e in interface.enums.iter() {
505            if e.bitfield {
506                self.codegen_bitflags_enum(e)?;
507            } else {
508                self.codegen_value_enum(e)?;
509            }
510            self.codegen_enum_into_arg(e)?;
511        }
512        Ok(())
513    }
514
515    fn codegen_enum_into_arg(&mut self, e: &ast::Enum) -> IoResult {
516        writeln!(self.w, "impl Into<Arg> for {} {{", e.rust_name())?;
517        writeln!(self.w, "    fn into(self) -> Arg {{")?;
518        writeln!(self.w, "        Arg::Uint(self.bits())")?;
519        writeln!(self.w, "    }}")?;
520        writeln!(self.w, "}}")?;
521        Ok(())
522    }
523
524    fn codegen_value_enum(&mut self, e: &ast::Enum) -> IoResult {
525        if let Some(ref d) = e.description {
526            self.codegen_description(d, "")?;
527        }
528        writeln!(self.w, "#[derive(Copy, Clone, Debug, Eq, PartialEq)]")?;
529        writeln!(self.w, "#[repr(u32)]")?;
530        writeln!(self.w, "pub enum {} {{", e.rust_name())?;
531        for entry in e.entries.iter() {
532            if let Some(ref s) = entry.summary {
533                for l in s.lines() {
534                    writeln!(self.w, "    /// {},", l.trim())?;
535                }
536            }
537            writeln!(self.w, "    {} = {},", entry.rust_name(), entry.value)?;
538        }
539        writeln!(self.w, "}}")?;
540        writeln!(self.w, "")?;
541        writeln!(self.w, "impl {} {{", e.rust_name())?;
542        writeln!(self.w, "    pub fn from_bits(v: u32) -> Option<Self> {{")?;
543        writeln!(self.w, "        match v {{")?;
544        for entry in e.entries.iter() {
545            writeln!(
546                self.w,
547                "        {} => Some({}::{}),",
548                entry.value,
549                e.rust_name(),
550                entry.rust_name()
551            )?;
552        }
553        writeln!(self.w, "        _ => None,")?;
554        writeln!(self.w, "        }}")?;
555        writeln!(self.w, "    }}")?;
556        writeln!(self.w, "")?;
557        writeln!(self.w, "    pub fn bits(&self) -> u32 {{")?;
558        writeln!(self.w, "        *self as u32")?;
559        writeln!(self.w, "    }}")?;
560        writeln!(self.w, "}}")?;
561        Ok(())
562    }
563
564    fn codegen_bitflags_enum(&mut self, e: &ast::Enum) -> IoResult {
565        writeln!(self.w, "::bitflags::bitflags! {{")?;
566        if let Some(ref d) = e.description {
567            self.codegen_description(d, "    ")?;
568        }
569        writeln!(
570            self.w,
571            "    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]"
572        )?;
573        writeln!(self.w, "    pub struct {}: u32 {{", e.rust_name())?;
574        for entry in e.entries.iter() {
575            if let Some(ref s) = entry.summary {
576                for l in s.lines() {
577                    writeln!(self.w, "        /// {},", l.trim())?;
578                }
579            }
580            writeln!(self.w, "        const {} = {};", entry.rust_name(), entry.value)?;
581        }
582        writeln!(self.w, "    }}")?;
583        writeln!(self.w, "}}")?;
584        Ok(())
585    }
586
587    fn codegen_description(&mut self, d: &ast::Description, prefix: &str) -> IoResult {
588        writeln!(self.w, "")?;
589        for s in d.summary.as_str().trim().lines() {
590            writeln!(self.w, "{}/// {}", prefix, s.trim())?;
591        }
592        writeln!(self.w, "{}///", prefix)?;
593        for s in d.description.trim().lines() {
594            writeln!(self.w, "{}/// {}", prefix, s.trim())?;
595        }
596        Ok(())
597    }
598}
599
600fn to_camel_case(s: &str) -> String {
601    s.split('_').filter(|s| s.len() > 0).map(|s| s[..1].to_uppercase() + &s[1..]).collect()
602}
603
604/// Enums can be referenced outside of the interface that defines them. When
605/// arguments are tagged with an enum, they'll provide the path to the enum
606/// in the form <interface>.<enum>.
607///
608/// For example, 'wl_output.transform' refers to the enum named 'transform'
609/// that's defined in the 'wl_output' interface.
610///
611/// Since our rust modules already mirror this structure, we can simply use a
612/// relative path to the enum when it's defined in the same interface, or
613/// reference the interface module for foreign enums.
614///
615/// Ex:
616///   Within the 'wl_output' module, the 'transform' enum can be referred to
617///   as just 'Transform'.
618///
619///   When 'wl_output' is referred to from another module we can use the crate-
620///   relative path 'crate::wl_output::Transform'.
621///
622/// Note we could always use the crate-relative path, but that would require
623/// passing the 'interface' parameter around to lots of logic that otherwise
624/// doesn't care.
625fn enum_path(name: &str) -> String {
626    let parts: Vec<&str> = name.splitn(2, ".").collect();
627    if parts.len() == 1 {
628        to_camel_case(name)
629    } else {
630        format!("crate::{}::{}", parts[0], to_camel_case(parts[1]))
631    }
632}
633
634type ArgFormatterFn = for<'r> fn(&'r ast::Arg) -> Cow<'r, str>;
635
636fn format_dispatch_arg_rust(arg: &ast::Arg) -> Cow<'_, str> {
637    if let Some(ref enum_type) = arg.enum_type {
638        return format!("Enum<{}>", enum_path(enum_type)).into();
639    }
640    match arg.kind {
641        ArgKind::Int => "i32".into(),
642        ArgKind::Uint => "u32".into(),
643        ArgKind::Fixed => "Fixed".into(),
644        ArgKind::String => "String".into(),
645        ArgKind::Object => "ObjectId".into(),
646        ArgKind::NewId => {
647            if let Some(interface) = &arg.interface {
648                format!("NewObject<{}>", to_camel_case(&interface)).into()
649            } else {
650                "ObjectId".into()
651            }
652        }
653        ArgKind::Array => "Array".into(),
654        ArgKind::Fd => "zx::Handle".into(),
655    }
656}
657
658fn format_wire_arg_rust(arg: &ast::Arg) -> Cow<'_, str> {
659    if let Some(ref enum_type) = arg.enum_type {
660        return enum_path(enum_type).into();
661    }
662    match arg.kind {
663        ArgKind::Int => "i32",
664        ArgKind::Uint => "u32",
665        ArgKind::Fixed => "Fixed",
666        ArgKind::String => "String",
667        ArgKind::Object => "ObjectId",
668        ArgKind::NewId => "NewId",
669        ArgKind::Array => "Array",
670        ArgKind::Fd => "zx::Handle",
671    }
672    .into()
673}
674
675fn format_arg_kind(arg: &ast::Arg) -> &'static str {
676    if arg.enum_type.is_some() {
677        return "ArgKind::Uint";
678    }
679    match arg.kind {
680        ArgKind::Int => "ArgKind::Int",
681        ArgKind::Uint => "ArgKind::Uint",
682        ArgKind::Fixed => "ArgKind::Fixed",
683        ArgKind::String => "ArgKind::String",
684        ArgKind::Object => "ArgKind::Object",
685        ArgKind::NewId => "ArgKind::NewId",
686        ArgKind::Array => "ArgKind::Array",
687        ArgKind::Fd => "ArgKind::Handle",
688    }
689}
690
691fn format_wire_arg(arg: &ast::Arg, var: &str) -> String {
692    if arg.enum_type.is_some() {
693        return format!("Arg::Uint({}.bits())", var);
694    }
695    match arg.kind {
696        ArgKind::Int => format!("Arg::Int({})", var),
697        ArgKind::Uint => format!("Arg::Uint({})", var),
698        ArgKind::Fixed => format!("Arg::Fixed({})", var),
699        ArgKind::String => format!("Arg::String({})", var),
700        ArgKind::Object => format!("Arg::Object({})", var),
701        ArgKind::NewId => format!("Arg::NewId({})", var),
702        ArgKind::Array => format!("Arg::Array({})", var),
703        ArgKind::Fd => format!("Arg::Handle({})", var),
704    }
705}
706
707fn arg_to_primitive(arg: &ast::Arg) -> String {
708    if let Some(ref enum_type) = arg.enum_type {
709        return format!(
710            "as_uint().map(|i| match {}::from_bits(i) {{
711                                      Some(e) => Enum::Recognized(e),
712                                      None => Enum::Unrecognized(i),
713                                 }})?",
714            enum_path(enum_type)
715        );
716    }
717    match arg.kind {
718        ArgKind::Int => "as_int()?",
719        ArgKind::Uint => "as_uint()?",
720        ArgKind::Fixed => "as_fixed()?.into()",
721        ArgKind::String => "as_string()?",
722        ArgKind::Object => "as_object()?",
723        ArgKind::NewId => "as_new_id()?.into()",
724        ArgKind::Array => "as_array()?",
725        ArgKind::Fd => "as_handle()?",
726    }
727    .to_string()
728}
729
730/// Helper trait for transforming wayland protocol names into the rust
731/// counterparts.
732///
733/// Ex, wl_display is written WlDisplay in rust code.
734trait RustName {
735    fn rust_name(&self) -> String;
736}
737
738impl RustName for ast::EnumEntry {
739    // some wayland enums are just numbers, which would result in illegal rust
740    // symbols. If we see a name that starts with a number we'll just prefix
741    // with '_'.
742    fn rust_name(&self) -> String {
743        let is_digit = self.name.chars().next().map_or(false, |c| c.is_digit(10));
744        let prefix = if is_digit { "_" } else { "" };
745        format!("{}{}", prefix, to_camel_case(&self.name))
746    }
747}
748
749fn is_rust_keyword(s: &str) -> bool {
750    match s {
751        "as" | "break" | "const" | "continue" | "crate" | "dyn" | "else" | "enum" | "extern"
752        | "false" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod"
753        | "move" | "mut" | "pub" | "ref" | "return" | "Self" | "self" | "static" | "struct"
754        | "super" | "trait" | "true" | "type" | "unsafe" | "use" | "where" | "while"
755        | "abstract" | "async" | "await" | "become" | "box" | "do" | "final" | "macro"
756        | "override" | "priv" | "try" | "typeof" | "unsized" | "virtual" | "yield" => true,
757        _ => false,
758    }
759}
760
761impl RustName for ast::Arg {
762    fn rust_name(&self) -> String {
763        if is_rust_keyword(&self.name) {
764            format!("{}_", self.name)
765        } else {
766            self.name.to_owned()
767        }
768    }
769}
770
771impl RustName for ast::Message {
772    fn rust_name(&self) -> String {
773        to_camel_case(&self.name)
774    }
775}
776
777impl RustName for ast::Enum {
778    fn rust_name(&self) -> String {
779        to_camel_case(&self.name)
780    }
781}
782
783impl RustName for ast::Interface {
784    fn rust_name(&self) -> String {
785        to_camel_case(&self.name)
786    }
787}