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 | |
39 | use std::{ffi::OsString, path::PathBuf}; |
40 | |
41 | mod c_interfaces; |
42 | mod client_gen; |
43 | mod common; |
44 | mod interfaces; |
45 | mod parse; |
46 | mod protocol; |
47 | mod server_gen; |
48 | mod token; |
49 | mod util; |
50 | |
51 | /// Proc-macro for generating low-level interfaces associated with an XML specification |
52 | #[proc_macro ] |
53 | pub 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 ] |
72 | pub 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 ] |
91 | pub 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)] |
109 | fn 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)] |
136 | enum Side { |
137 | /// wayland client applications |
138 | Client, |
139 | /// wayland compositors |
140 | Server, |
141 | } |
142 | |