wayland_scanner/
lib.rs

1//! Wayland protocol code-generation machinnery
2//!
3//! This crate provides procedural macros for generating the rust code associated with a
4//! Wayland XML protocol specification, for use with the `wayland-client`, `wayland-server`
5//! and `wayland-backend` crates.
6//!
7//! Before trying to use this crate, you may check if the protocol extension you want to use
8//! is not already exposed in the `wayland-protocols` crate.
9//!
10//! ## Example usage
11//!
12//! Below is a template for generating the code for a custom protocol client-side. Server-side
13//! is identical, just replacing `client` by `server`. The path to the XML file is relative to the
14//! crate root.
15//!
16//! ```rust,ignore
17//! // Generate the bindings in their own module
18//! pub mod my_protocol {
19//!     use wayland_client;
20//!     // import objects from the core protocol if needed
21//!     use wayland_client::protocol::*;
22//!
23//!     // This module hosts a low-level representation of the protocol objects
24//!     // you will not need to interact with it yourself, but the code generated
25//!     // by the generate_client_code! macro will use it
26//!     pub mod __interfaces {
27//!         // import the interfaces from the core protocol if needed
28//!         use wayland_client::protocol::__interfaces::*;
29//!         wayland_scanner::generate_interfaces!("./path/to/the/protocol.xml");
30//!     }
31//!     use self::__interfaces::*;
32//!
33//!     // This macro generates the actual types that represent the wayland objects of
34//!     // your custom protocol
35//!     wayland_scanner::generate_client_code!("./path/to/the/protocol.xml");
36//! }
37//! ```
38
39use std::{ffi::OsString, path::PathBuf};
40
41mod c_interfaces;
42mod client_gen;
43mod common;
44mod interfaces;
45mod parse;
46mod protocol;
47mod server_gen;
48mod token;
49mod util;
50
51/// Proc-macro for generating low-level interfaces associated with an XML specification
52#[proc_macro]
53pub fn generate_interfaces(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
54    let path: OsString = token::parse_lit_str_token(stream).into();
55    let path = if let Some(manifest_dir) = std::env::var_os("CARGO_MANIFEST_DIR") {
56        let mut buf = PathBuf::from(manifest_dir);
57        buf.push(path);
58        buf
59    } else {
60        path.into()
61    };
62    let file = match std::fs::File::open(&path) {
63        Ok(file) => file,
64        Err(e) => panic!("Failed to open protocol file {}: {}", path.display(), e),
65    };
66    let protocol = parse::parse(file);
67    interfaces::generate(&protocol, true).into()
68}
69
70/// Proc-macro for generating client-side API associated with an XML specification
71#[proc_macro]
72pub fn generate_client_code(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
73    let path: OsString = token::parse_lit_str_token(stream).into();
74    let path = if let Some(manifest_dir) = std::env::var_os("CARGO_MANIFEST_DIR") {
75        let mut buf = PathBuf::from(manifest_dir);
76        buf.push(path);
77        buf
78    } else {
79        path.into()
80    };
81    let file = match std::fs::File::open(&path) {
82        Ok(file) => file,
83        Err(e) => panic!("Failed to open protocol file {}: {}", path.display(), e),
84    };
85    let protocol = parse::parse(file);
86    client_gen::generate_client_objects(&protocol).into()
87}
88
89/// Proc-macro for generating server-side API associated with an XML specification
90#[proc_macro]
91pub fn generate_server_code(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
92    let path: OsString = token::parse_lit_str_token(stream).into();
93    let path = if let Some(manifest_dir) = std::env::var_os("CARGO_MANIFEST_DIR") {
94        let mut buf = PathBuf::from(manifest_dir);
95        buf.push(path);
96        buf
97    } else {
98        path.into()
99    };
100    let file = match std::fs::File::open(&path) {
101        Ok(file) => file,
102        Err(e) => panic!("Failed to open protocol file {}: {}", path.display(), e),
103    };
104    let protocol = parse::parse(file);
105    server_gen::generate_server_objects(&protocol).into()
106}
107
108#[cfg(test)]
109fn format_rust_code(code: &str) -> String {
110    use std::{
111        io::Write,
112        process::{Command, Stdio},
113    };
114    if let Ok(mut proc) = Command::new("rustfmt")
115        .arg("--emit=stdout")
116        .arg("--edition=2018")
117        .stdin(Stdio::piped())
118        .stdout(Stdio::piped())
119        //.stderr(Stdio::null())
120        .spawn()
121    {
122        {
123            let stdin = proc.stdin.as_mut().unwrap();
124            stdin.write_all(code.as_bytes()).unwrap();
125        }
126        if let Ok(output) = proc.wait_with_output() {
127            if output.status.success() {
128                return std::str::from_utf8(&output.stdout).unwrap().to_owned();
129            }
130        }
131    }
132    panic!("Rustfmt failed!");
133}
134
135#[derive(Copy, Clone, PartialEq, Eq, Debug)]
136enum Side {
137    /// wayland client applications
138    Client,
139    /// wayland compositors
140    Server,
141}