| 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 | |