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: PathBuf = if let Some(manifest_dir: OsString) = std::env::var_os(key:"CARGO_MANIFEST_DIR") {
56 let mut buf: PathBuf = PathBuf::from(manifest_dir);
57 buf.push(path);
58 buf
59 } else {
60 path.into()
61 };
62 let file: File = match std::fs::File::open(&path) {
63 Ok(file: File) => file,
64 Err(e: Error) => panic!("Failed to open protocol file {}: {}", path.display(), e),
65 };
66 let protocol: Protocol = parse::parse(stream:file);
67 interfaces::generate(&protocol, with_c_interfaces: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: PathBuf = if let Some(manifest_dir: OsString) = std::env::var_os(key:"CARGO_MANIFEST_DIR") {
75 let mut buf: PathBuf = PathBuf::from(manifest_dir);
76 buf.push(path);
77 buf
78 } else {
79 path.into()
80 };
81 let file: File = match std::fs::File::open(&path) {
82 Ok(file: File) => file,
83 Err(e: Error) => panic!("Failed to open protocol file {}: {}", path.display(), e),
84 };
85 let protocol: Protocol = parse::parse(stream: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: PathBuf = if let Some(manifest_dir: OsString) = std::env::var_os(key:"CARGO_MANIFEST_DIR") {
94 let mut buf: PathBuf = PathBuf::from(manifest_dir);
95 buf.push(path);
96 buf
97 } else {
98 path.into()
99 };
100 let file: File = match std::fs::File::open(&path) {
101 Ok(file: File) => file,
102 Err(e: Error) => panic!("Failed to open protocol file {}: {}", path.display(), e),
103 };
104 let protocol: Protocol = parse::parse(stream: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}
142