#![recursion_limit = "128"]
#![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)]
#![allow(non_upper_case_globals, non_snake_case, unused_imports)]
#![allow(deprecated, missing_docs)]
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate xml;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::path::Path;
use std::process::Command;
use xml::attribute::OwnedAttribute;
use xml::reader::ParserConfig;
use xml::reader::XmlEvent;
use xml::EventReader;
use std::env::var;
use std::iter;
use proc_macro2::{Ident, Literal, Span, TokenStream};
use std::cmp;
use std::iter::repeat;
use std::ascii::AsciiExt;
use quote::ToTokens;
#[derive(Clone, Debug)]
pub struct Protocol {
pub name: String,
pub copyright: Option<String>,
pub description: Option<(String, String)>,
pub interfaces: Vec<Interface>,
}
impl Protocol {
pub fn new(name: String) -> Protocol {
Protocol {
name,
copyright: None,
description: None,
interfaces: Vec::new(),
}
}
}
#[derive(Clone, Debug)]
pub struct Interface {
pub name: String,
pub version: u32,
pub description: Option<(String, String)>,
pub requests: Vec<Message>,
pub events: Vec<Message>,
pub enums: Vec<Enum>,
}
impl Interface {
pub fn new() -> Interface {
Interface {
name: String::new(),
version: 1,
description: None,
requests: Vec::new(),
events: Vec::new(),
enums: Vec::new(),
}
}
}
#[derive(Clone, Debug)]
pub struct Message {
pub name: String,
pub typ: Option<Type>,
pub since: u32,
pub description: Option<(String, String)>,
pub args: Vec<Arg>,
pub type_index: usize,
}
impl Message {
pub fn new() -> Message {
Message {
name: String::new(),
typ: None,
since: 1,
description: None,
args: Vec::new(),
type_index: 0,
}
}
pub fn all_null(&self) -> bool {
self.args
.iter()
.all(|a| !((a.typ == Type::Object || a.typ == Type::NewId) && a.interface.is_some()))
}
}
#[derive(Clone, Debug)]
pub struct Arg {
pub name: String,
pub typ: Type,
pub interface: Option<String>,
pub summary: Option<String>,
pub description: Option<(String, String)>,
pub allow_null: bool,
pub enum_: Option<String>,
}
impl Arg {
pub fn new() -> Arg {
Arg {
name: String::new(),
typ: Type::Object,
interface: None,
summary: None,
description: None,
allow_null: false,
enum_: None,
}
}
}
#[derive(Clone, Debug)]
pub struct Enum {
pub name: String,
pub since: u16,
pub description: Option<(String, String)>,
pub entries: Vec<Entry>,
pub bitfield: bool,
}
impl Enum {
pub fn new() -> Enum {
Enum {
name: String::new(),
since: 1,
description: None,
entries: Vec::new(),
bitfield: false,
}
}
}
#[derive(Clone, Debug)]
pub struct Entry {
pub name: String,
pub value: u32,
pub since: u16,
pub description: Option<(String, String)>,
pub summary: Option<String>,
}
impl Entry {
pub fn new() -> Entry {
Entry {
name: String::new(),
value: 0,
since: 1,
description: None,
summary: None,
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Type {
Int,
Uint,
Fixed,
String,
Object,
NewId,
Array,
Fd,
Destructor,
}
impl Type {
pub fn nullable(self) -> bool {
match self {
Type::String | Type::Object | Type::NewId | Type::Array => true,
_ => false,
}
}
pub fn rust_type(self) -> TokenStream {
match self {
Type::Int => quote!(i32),
Type::Uint => quote!(u32),
Type::Fixed => quote!(wl_fixed_t),
Type::Array => quote!(*mut wl_array),
Type::Fd => quote!(::std::os::unix::io::RawFd),
Type::String => quote!(*mut c_char),
Type::Object => quote!(*mut wl_proxy),
_ => quote!(()),
}
}
pub fn common_type(self) -> TokenStream {
match self {
Type::Int => quote!(Int),
Type::Uint => quote!(Uint),
Type::Fixed => quote!(Fixed),
Type::Array => quote!(Array),
Type::Fd => quote!(Fd),
Type::String => quote!(Str),
Type::Object => quote!(Object),
Type::NewId => quote!(NewId),
Type::Destructor => panic!("Destructor is not a valid argument type."),
}
}
}
macro_rules! extract_from(
($it: expr => $pattern: pat => $result: expr) => (
match $it.next() {
Ok($pattern) => { $result },
e => panic!("Ill-formed protocol file: {:?}", e)
}
)
);
macro_rules! extract_end_tag(
($it: expr => $tag: expr) => (
extract_from!($it => XmlEvent::EndElement { name } => {
assert!(name.local_name == $tag, "Ill-formed protocol file");
});
)
);
pub fn parse_stream<S: Read>(stream: S) -> Protocol {
let mut reader =
EventReader::new_with_config(stream, ParserConfig::new().trim_whitespace(true));
reader.next().expect("Could not read from event reader");
parse_protocol(reader)
}
fn parse_protocol<R: Read>(mut reader: EventReader<R>) -> Protocol {
let mut protocol = extract_from!(
reader => XmlEvent::StartElement { name, attributes, .. } => {
assert!(name.local_name == "protocol", "Missing protocol toplevel tag");
assert!(attributes[0].name.local_name == "name", "Protocol must have a name");
Protocol::new(attributes[0].value.clone())
}
);
loop {
match reader.next() {
Ok(XmlEvent::StartElement {
name, attributes, ..
}) => {
match &name.local_name[..] {
"copyright" => {
let copyright = match reader.next() {
Ok(XmlEvent::Characters(copyright))
| Ok(XmlEvent::CData(copyright)) => copyright,
e => panic!("Ill-formed protocol file: {:?}", e),
};
extract_end_tag!(reader => "copyright");
protocol.copyright = Some(copyright);
}
"interface" => {
protocol
.interfaces
.push(parse_interface(&mut reader, attributes));
}
"description" => {
protocol.description = Some(parse_description(&mut reader, attributes));
}
_ => panic!(
"Ill-formed protocol file: unexpected token `{}` in protocol {}",
name.local_name, protocol.name
),
}
}
Ok(XmlEvent::EndElement { name }) => {
assert!(
name.local_name == "protocol",
"Unexpected closing token `{}`",
name.local_name
);
break;
}
e => panic!("Ill-formed protocol file: {:?}", e),
}
}
protocol
}
fn parse_interface<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> Interface {
let mut interface = Interface::new();
for attr in attrs {
match &attr.name.local_name[..] {
"name" => interface.name = attr.value,
"version" => interface.version = attr.value.parse().unwrap(),
_ => {}
}
}
loop {
match reader.next() {
Ok(XmlEvent::StartElement {
name, attributes, ..
}) => match &name.local_name[..] {
"description" => {
interface.description = Some(parse_description(reader, attributes))
}
"request" => interface.requests.push(parse_request(reader, attributes)),
"event" => interface.events.push(parse_event(reader, attributes)),
"enum" => interface.enums.push(parse_enum(reader, attributes)),
_ => panic!("Unexpected tocken: `{}`", name.local_name),
},
Ok(XmlEvent::EndElement { ref name }) if name.local_name == "interface" => break,
_ => {}
}
}
interface
}
fn parse_description<R: Read>(
reader: &mut EventReader<R>,
attrs: Vec<OwnedAttribute>,
) -> (String, String) {
let mut summary = String::new();
for attr in attrs {
if &attr.name.local_name[..] == "summary" {
summary = attr.value.split_whitespace().collect::<Vec<_>>().join(" ");
}
}
let description = match reader.next() {
Ok(XmlEvent::Characters(txt)) => {
extract_end_tag!(reader => "description");
txt
}
Ok(XmlEvent::EndElement { ref name }) if name.local_name == "description" => String::new(),
e => panic!("Ill-formed protocol file: {:?}", e),
};
(summary, description)
}
fn parse_request<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> Message {
let mut request = Message::new();
for attr in attrs {
match &attr.name.local_name[..] {
"name" => request.name = attr.value,
"type" => request.typ = Some(parse_type(&attr.value)),
"since" => request.since = attr.value.parse().unwrap(),
_ => {}
}
}
loop {
match reader.next() {
Ok(XmlEvent::StartElement {
name, attributes, ..
}) => match &name.local_name[..] {
"description" => request.description = Some(parse_description(reader, attributes)),
"arg" => request.args.push(parse_arg(reader, attributes)),
_ => panic!("Unexpected tocken: `{}`", name.local_name),
},
Ok(XmlEvent::EndElement { ref name }) if name.local_name == "request" => break,
_ => {}
}
}
request
}
fn parse_enum<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> Enum {
let mut enu = Enum::new();
for attr in attrs {
match &attr.name.local_name[..] {
"name" => enu.name = attr.value,
"since" => enu.since = attr.value.parse().unwrap(),
"bitfield" => {
if &attr.value[..] == "true" {
enu.bitfield = true
}
}
_ => {}
}
}
loop {
match reader.next() {
Ok(XmlEvent::StartElement {
name, attributes, ..
}) => match &name.local_name[..] {
"description" => enu.description = Some(parse_description(reader, attributes)),
"entry" => enu.entries.push(parse_entry(reader, attributes)),
_ => panic!("Unexpected tocken: `{}`", name.local_name),
},
Ok(XmlEvent::EndElement { ref name }) if name.local_name == "enum" => break,
_ => {}
}
}
enu
}
fn parse_event<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> Message {
let mut event = Message::new();
for attr in attrs {
match &attr.name.local_name[..] {
"name" => event.name = attr.value,
"since" => event.since = attr.value.parse().unwrap(),
_ => {}
}
}
loop {
match reader.next() {
Ok(XmlEvent::StartElement {
name, attributes, ..
}) => match &name.local_name[..] {
"description" => event.description = Some(parse_description(reader, attributes)),
"arg" => event.args.push(parse_arg(reader, attributes)),
_ => panic!("Unexpected tocken: `{}`", name.local_name),
},
Ok(XmlEvent::EndElement { ref name }) if name.local_name == "event" => break,
_ => {}
}
}
event
}
fn parse_arg<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> Arg {
let mut arg = Arg::new();
for attr in attrs {
match &attr.name.local_name[..] {
"name" => arg.name = attr.value,
"type" => arg.typ = parse_type(&attr.value),
"summary" => {
arg.summary = Some(attr.value.split_whitespace().collect::<Vec<_>>().join(" "))
}
"interface" => arg.interface = Some(attr.value),
"allow-null" => {
if attr.value == "true" {
arg.allow_null = true
}
}
"enum" => arg.enum_ = Some(attr.value),
_ => {}
}
}
loop {
match reader.next() {
Ok(XmlEvent::StartElement {
name, attributes, ..
}) => match &name.local_name[..] {
"description" => arg.description = Some(parse_description(reader, attributes)),
_ => panic!("Unexpected tocken: `{}`", name.local_name),
},
Ok(XmlEvent::EndElement { ref name }) if name.local_name == "arg" => break,
_ => {}
}
}
arg
}
fn parse_type(txt: &str) -> Type {
match txt {
"int" => Type::Int,
"uint" => Type::Uint,
"fixed" => Type::Fixed,
"string" => Type::String,
"object" => Type::Object,
"new_id" => Type::NewId,
"array" => Type::Array,
"fd" => Type::Fd,
"destructor" => Type::Destructor,
e => panic!("Unexpected type: {}", e),
}
}
fn parse_entry<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> Entry {
let mut entry = Entry::new();
for attr in attrs {
match &attr.name.local_name[..] {
"name" => entry.name = attr.value,
"value" => {
entry.value = if attr.value.starts_with("0x") {
u32::from_str_radix(&attr.value[2..], 16).unwrap()
} else {
attr.value.parse().unwrap()
};
}
"since" => entry.since = attr.value.parse().unwrap(),
"summary" => {
entry.summary = Some(attr.value.split_whitespace().collect::<Vec<_>>().join(" "))
}
_ => {}
}
}
loop {
match reader.next() {
Ok(XmlEvent::StartElement {
name, attributes, ..
}) => match &name.local_name[..] {
"description" => entry.description = Some(parse_description(reader, attributes)),
_ => panic!("Unexpected tocken: `{}`", name.local_name),
},
Ok(XmlEvent::EndElement { ref name }) if name.local_name == "entry" => break,
_ => {}
}
}
entry
}
fn load_xml<P: AsRef<Path>>(prot: P) -> Protocol {
let pfile = File::open(prot.as_ref()).unwrap_or_else(|_| {
panic!(
"Unable to open protocol file `{}`.",
prot.as_ref().display()
)
});
parse_stream(pfile)
}
pub(crate) fn to_doc_attr(text: &str) -> TokenStream {
let text = text.lines().map(str::trim).collect::<Vec<_>>().join("\n");
let text = text.trim();
quote!(#[doc = #text])
}
pub(crate) fn description_to_doc_attr(&(ref short, ref long): &(String, String)) -> TokenStream {
to_doc_attr(&format!("{}\n\n{}", short, long))
}
pub fn null_terminated_byte_string_literal(string: &str) -> Literal {
let mut val = Vec::with_capacity(string.len() + 1);
val.extend_from_slice(string.as_bytes());
val.push(0);
Literal::byte_string(&val)
}
pub(crate) fn generate_interfaces_prefix<'a, T: Iterator<Item = &'a Protocol>>(
protocols: T,
) -> TokenStream {
let longest_nulls = protocols
.map(|protocol| {
protocol.interfaces.iter().fold(0, |max, interface| {
let request_longest_null = interface.requests.iter().fold(0, |max, request| {
if request.all_null() {
cmp::max(request.args.len(), max)
} else {
max
}
});
let events_longest_null = interface.events.iter().fold(0, |max, event| {
if event.all_null() {
cmp::max(event.args.len(), max)
} else {
max
}
});
cmp::max(max, cmp::max(request_longest_null, events_longest_null))
})
})
.max()
.unwrap_or(1);
let types_null_len = Literal::usize_unsuffixed(longest_nulls);
let nulls = repeat(quote!(NULLPTR as *const sys::common::wl_interface)).take(longest_nulls);
quote! {
use std::os::raw::{c_char, c_void};
const NULLPTR: *const c_void = 0 as *const c_void;
static mut types_null: [*const sys::common::wl_interface; #types_null_len] = [
#(#nulls,)*
];
}
}
pub(crate) fn generate_interface(interface: &Interface) -> TokenStream {
let requests = gen_messages(interface, &interface.requests, "requests");
let events = gen_messages(interface, &interface.events, "events");
let interface_ident = Ident::new(&format!("{}_interface", interface.name), Span::call_site());
let name_value = null_terminated_byte_string_literal(&interface.name);
let version_value = Literal::i32_unsuffixed(interface.version as i32);
let request_count_value = Literal::i32_unsuffixed(interface.requests.len() as i32);
let requests_value = if interface.requests.is_empty() {
quote!(NULLPTR as *const wl_message)
} else {
let requests_ident = Ident::new(&format!("{}_requests", interface.name), Span::call_site());
quote!(unsafe { &#requests_ident as *const _ })
};
let event_count_value = Literal::i32_unsuffixed(interface.events.len() as i32);
let events_value = if interface.events.is_empty() {
quote!(NULLPTR as *const wl_message)
} else {
let events_ident = Ident::new(&format!("{}_events", interface.name), Span::call_site());
quote!(unsafe { &#events_ident as *const _ })
};
quote!(
#requests
#events
pub static mut #interface_ident: wl_interface = wl_interface {
name: #name_value as *const u8 as *const c_char,
version: #version_value,
request_count: #request_count_value,
requests: #requests_value,
event_count: #event_count_value,
events: #events_value,
};
)
}
fn gen_messages(interface: &Interface, messages: &[Message], which: &str) -> TokenStream {
if messages.is_empty() {
return TokenStream::new();
}
let types_arrays = messages.iter().filter_map(|msg| {
if msg.all_null() {
None
} else {
let array_ident = Ident::new(
&format!("{}_{}_{}_types", interface.name, which, msg.name),
Span::call_site(),
);
let array_len = Literal::usize_unsuffixed(msg.args.len());
let array_values = msg.args.iter().map(|arg| match (arg.typ, &arg.interface) {
(Type::Object, &Some(ref inter)) | (Type::NewId, &Some(ref inter)) => {
let module = Ident::new(inter, Span::call_site());
let interface_ident =
Ident::new(&format!("{}_interface", inter), Span::call_site());
quote!(unsafe { &super::#module::#interface_ident as *const wl_interface })
}
_ => quote!(NULLPTR as *const wl_interface),
});
Some(quote! {
static mut #array_ident: [*const wl_interface; #array_len] = [
#(#array_values,)*
];
})
}
});
let message_array_ident =
Ident::new(&format!("{}_{}", interface.name, which), Span::call_site());
let message_array_len = Literal::usize_unsuffixed(messages.len());
let message_array_values = messages.iter().map(|msg| {
let name_value = null_terminated_byte_string_literal(&msg.name);
let signature_value = Literal::byte_string(&message_signature(msg));
let types_ident = if msg.all_null() {
Ident::new("types_null", Span::call_site())
} else {
Ident::new(
&format!("{}_{}_{}_types", interface.name, which, msg.name),
Span::call_site(),
)
};
quote! {
wl_message {
name: #name_value as *const u8 as *const c_char,
signature: #signature_value as *const u8 as *const c_char,
types: unsafe { &#types_ident as *const _ },
}
}
});
quote! {
#(#types_arrays)*
pub static mut #message_array_ident: [wl_message; #message_array_len] = [
#(#message_array_values,)*
];
}
}
fn message_signature(msg: &Message) -> Vec<u8> {
let mut res = Vec::new();
if msg.since > 1 {
res.extend_from_slice(msg.since.to_string().as_bytes());
}
for arg in &msg.args {
if arg.typ.nullable() && arg.allow_null {
res.push(b'?');
}
match arg.typ {
Type::NewId => {
if arg.interface.is_none() {
res.extend_from_slice(b"su");
}
res.push(b'n');
}
Type::Uint => res.push(b'u'),
Type::Fixed => res.push(b'f'),
Type::String => res.push(b's'),
Type::Object => res.push(b'o'),
Type::Array => res.push(b'a'),
Type::Fd => res.push(b'h'),
Type::Int => res.push(b'i'),
_ => {}
}
}
res.push(0);
res
}
pub fn is_keyword(txt: &str) -> bool {
match txt {
"abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue"
| "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if"
| "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut"
| "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" | "return"
| "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" | "true"
| "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while"
| "yield" | "__handler" | "__object" => true,
_ => false,
}
}
fn fix_ident(input: &str) -> String {
if is_keyword(input) {
format!("_{}", input)
} else {
input.to_owned()
}
}
impl ToTokens for Enum {
fn to_tokens(&self, tokens: &mut TokenStream) {
let enum_decl;
let enum_impl;
let doc_attr = self.description.as_ref().map(description_to_doc_attr);
let ident = Ident::new(&fix_ident(&self.name), Span::call_site());
if self.bitfield {
let entries = self.entries.iter().map(|entry| {
let doc_attr = entry
.description
.as_ref()
.map(description_to_doc_attr)
.or_else(|| entry.summary.as_ref().map(|s| to_doc_attr(s)));
let prefix = if entry.name.chars().next().unwrap().is_numeric() {
"_"
} else {
""
};
let ident = Ident::new(
&fix_ident(&format!("{}{}", prefix, entry.name)),
Span::call_site(),
);
let value = Literal::u32_unsuffixed(entry.value);
quote! {
#doc_attr
const #ident = #value;
}
});
enum_decl = quote! {
bitflags! {
#doc_attr
pub struct #ident: u32 {
#(#entries)*
}
}
};
enum_impl = quote! {
impl #ident {
pub fn from_raw(n: u32) -> Option<#ident> {
Some(#ident::from_bits_truncate(n))
}
pub fn to_raw(&self) -> u32 {
self.bits()
}
}
};
} else {
let variants = self.entries.iter().map(|entry| {
let doc_attr = entry
.description
.as_ref()
.map(description_to_doc_attr)
.or_else(|| entry.summary.as_ref().map(|s| to_doc_attr(s)));
let prefix = if entry.name.chars().next().unwrap().is_numeric() {
"_"
} else {
""
};
let variant = Ident::new(
&fix_ident(&format!("{}{}", prefix, entry.name)),
Span::call_site(),
);
let value = Literal::u32_unsuffixed(entry.value);
quote! {
#doc_attr
#variant = #value
}
});
enum_decl = quote! {
#doc_attr
#[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum #ident {
#(#variants,)*
#[doc(hidden)]
__nonexhaustive,
}
};
let match_arms = self.entries.iter().map(|entry| {
let value = Literal::u32_unsuffixed(entry.value);
let prefix = if entry.name.chars().next().unwrap().is_numeric() {
"_"
} else {
""
};
let variant = Ident::new(
&fix_ident(&format!("{}{}", prefix, entry.name)),
Span::call_site(),
);
quote! {
#value => Some(#ident::#variant)
}
});
enum_impl = quote! {
impl #ident {
pub fn from_raw(n: u32) -> Option<#ident> {
match n {
#(#match_arms,)*
_ => Option::None
}
}
pub fn to_raw(&self) -> u32 {
*self as u32
}
}
};
}
enum_decl.to_tokens(tokens);
enum_impl.to_tokens(tokens);
}
}
pub fn dotted_to_relname(input: &str) -> TokenStream {
let mut it = input.split('.');
match (it.next(), it.next()) {
(Some(module), Some(name)) => {
let module = Ident::new(module, Span::call_site());
let ident = Ident::new(&fix_ident(name), Span::call_site());
quote!(super::#module::#ident)
}
(Some(name), None) => Ident::new(&fix_ident(name), Span::call_site()).into_token_stream(),
_ => unreachable!(),
}
}
fn event_method_prototype(name: &Ident, msg: &Message, side: Side) -> TokenStream {
let method_name = Ident::new(
&format!(
"{}{}",
if is_keyword(&msg.name) { "_" } else { "" },
msg.name
),
Span::call_site(),
);
let method_args = msg.args.iter().map(|arg| {
let arg_name = Ident::new(
&format!(
"{}{}",
if is_keyword(&arg.name) || arg.name == "object" {
"_"
} else {
""
},
arg.name
),
Span::call_site(),
);
let arg_type_inner = if let Some(ref enu) = arg.enum_ {
dotted_to_relname(enu)
} else {
match arg.typ {
Type::Uint => quote!(u32),
Type::Int => quote!(i32),
Type::Fixed => quote!(wl_fixed_t),
Type::String => quote!(*mut c_char),
Type::Array => quote!(*mut wl_array),
Type::Fd => quote!(::std::os::unix::io::RawFd),
Type::Object => {
if let Some(ref iface) = arg.interface {
let iface_mod = Ident::new(&iface, Span::call_site());
let iface_type = Ident::new(&iface, Span::call_site());
quote!(*mut super::#iface_mod::#iface_type)
} else {
quote!(*mut wl_proxy)
}
}
Type::NewId => {
if let Some(ref iface) = arg.interface {
let iface_mod = Ident::new(&iface, Span::call_site());
let iface_type = Ident::new(&iface, Span::call_site());
quote!(*mut super::#iface_mod::#iface_type)
} else {
quote!((String, u32, *mut wl_proxy))
}
}
Type::Destructor => panic!("An argument cannot have type \"destructor\"."),
}
};
let field_type = if arg.allow_null {
quote!(Option<#arg_type_inner>)
} else {
arg_type_inner.into_token_stream()
};
quote! {
#arg_name: #field_type
}
});
quote! {
fn #method_name(&mut self, object: *mut #name, #(#method_args),*) {}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Side {
Client,
Server,
}
pub(crate) fn gen_event_handler_trait(
iname: &Ident,
messages: &[Message],
side: Side,
) -> TokenStream {
let methods = messages.iter().map(|msg| {
let mut docs = String::new();
if let Some((ref short, ref long)) = msg.description {
docs += &format!("{}\n\n{}\n", short, long);
}
if let Some(Type::Destructor) = msg.typ {
docs += "\nThis is a destructor, you cannot send requests to this object any longer once this method is called.";
}
if msg.since > 1 {
docs += &format!("\nOnly available since version {} of the interface.", msg.since);
}
let doc_attr = to_doc_attr(&docs);
let proto = event_method_prototype(iname, &msg, side);
quote! {
#doc_attr
#proto
}
});
let method_name = Ident::new(
&format!(
"{}_{}",
iname,
if side == Side::Server {
"interface"
} else {
"listener"
}
),
Span::call_site(),
);
match side {
Side::Client => quote! {
pub trait #method_name {
#(#methods)*
}
},
Side::Server => quote! {
pub trait #method_name {
#(#methods)*
}
},
}
}
fn generate_stubs(interface: &Interface, side: Side) -> TokenStream {
let list = if side == Side::Client {
&interface.requests
} else {
&interface.events
};
let sud = Ident::new(
&format!("{}_set_user_data", interface.name),
Span::call_site(),
);
let gud = Ident::new(
&format!("{}_get_user_data", interface.name),
Span::call_site(),
);
let gv = Ident::new(
&format!("{}_get_version", interface.name),
Span::call_site(),
);
let al = Ident::new(
&format!("{}_add_listener", interface.name),
Span::call_site(),
);
let arl = Ident::new(
&format!("{}_add_rust_listener", interface.name),
Span::call_site(),
);
let lt = Ident::new(
&format!("{}_listener", interface.name),
Span::call_site(),
);
let mut opcode = 0u32;
let mut has_destroy = false;
let interface_ident = Ident::new(&fix_ident(&interface.name), Span::call_site());
let thisparam = quote!(#interface_ident: *mut super::#interface_ident::#interface_ident);
let add_l_stub = if side == Side::Client {
quote! {
pub unsafe fn #al(#thisparam, listener: *mut c_void, data: *mut c_void) -> c_int {
return ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_add_listener,
#interface_ident as _, listener as _, data as _);
}
pub unsafe fn #arl(#thisparam, listener: & dyn #lt) -> bool {
let to = (listener as *const dyn #lt).to_raw_parts();
let op = to.0;
let fp = std::mem::transmute::<_, *mut c_void>(std::mem::transmute::<_, usize>(to.1) + (3 * std::mem::size_of::<usize>()) );
return #al(#interface_ident as _, fp as _, op as _) == 0;
}
}
} else {
quote!()
};
let stubs = list.iter().map(|msg| {
let result = match side {
Side::Client => {
let doc = msg.description.as_ref().map(description_to_doc_attr);
let name = Ident::new(&format!("{}_{}", interface.name, msg.name), Span::call_site());
let mut returnType = None;
let mut returnNewType = false;
let argsv = msg.args.iter().map(|arg| {
let mut bindlike = false;
let ty = match arg.typ {
Type::Uint => quote!(u32),
Type::Int => quote!(i32),
Type::Fixed => quote!(wl_fixed_t),
Type::String => quote!(*const c_char),
Type::Array => quote!(*mut wl_array),
Type::Fd => quote!(::std::os::unix::io::RawFd),
Type::Object => {
if let Some(ref iface) = arg.interface {
let iface_mod = Ident::new(&iface, Span::call_site());
let iface_type = Ident::new(&iface, Span::call_site());
quote!(*mut super::#iface_mod::#iface_type)
} else {
quote!(*mut wl_proxy)
}
}
Type::NewId => {
if let Some(ref iface) = arg.interface {
returnType = Some(iface);
quote!()
} else {
returnNewType = true;
bindlike = true;
quote!(*mut wl_interface, version: u32)
}
}
Type::Destructor => panic!("An argument cannot have type \"destructor\"."),
};
let an = Ident::new(&if bindlike { "interface".to_owned() } else { fix_ident(&arg.name) }, Span::call_site());
if !ty.is_empty() {
Some((an, ty, bindlike))
} else { None }
}).collect::<Vec<_>>();
let paramlist = argsv.iter().filter(|o| {o.is_some()}).map(|o|{o.as_ref().unwrap().clone()}).map(|pair| {let n = &pair.0; let t = &pair.1; quote!(#n: #t)});
let param_name_list = argsv.iter()
.map(|o|
{
if let Some(pair) = o {
if !pair.2 {
let n = &pair.0;
quote!(#n)
} else {
quote!( (*interface).name, version, std::ptr::null::<c_void>() )
}
} else {
quote!(std::ptr::null::<c_void>() )
}
});
let args = std::iter::once(thisparam.clone())
.chain(paramlist);
if msg.name == "destroy" {
has_destroy = true;
}
let destroy_end = if msg.typ == Some(Type::Destructor) {
quote!(ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy,
#interface_ident as _);)
} else {quote!()};
let func = if returnNewType {
quote! {
pub unsafe fn #name(#(#args),*) -> *mut wl_proxy {
let r = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal_constructor_versioned,
#interface_ident as _, #opcode, interface as _, version #(,#param_name_list)* );
#destroy_end
return r as _;
}
}
} else if let Some(iface) = returnType {
let iface_mod = Ident::new(&fix_ident(&iface), Span::call_site());
let iface_type = Ident::new(&fix_ident(&iface), Span::call_site());
let iface_name = Ident::new(&format!("{}_{}", &iface, "interface"), Span::call_site());
quote! {
pub unsafe fn #name(#(#args),*) -> *mut super::#iface_mod::#iface_type {
let r = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal_constructor,
#interface_ident as _, #opcode, &super::#iface_mod::#iface_name as * const _ #(,#param_name_list)* );
#destroy_end
return r as _;
}
}
}
else {
quote! {
pub unsafe fn #name(#(#args),*) {
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal,
#interface_ident as _, #opcode #(,#param_name_list)*, std::ptr::null::<c_void>() );
#destroy_end
}
}
};
quote! {
#doc
#func
}
},
Side::Server => { quote!() }
};
opcode += 1;
result
}).collect::<Vec<_>>();
let simple_desctr = if !has_destroy && interface.name != "wl_display" {
let dname = Ident::new(
&format!("{}_{}", interface.name, "destroy"),
Span::call_site(),
);
quote! {
pub unsafe fn #dname(#thisparam) {
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy,
#interface_ident as _);
}
}
} else {
quote!()
};
quote! {
pub unsafe fn #sud(#thisparam, user_data: *mut c_void) {
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_set_user_data,
#interface_ident as _, user_data);
}
pub unsafe fn #gud(#thisparam) -> * mut c_void {
return ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data,
#interface_ident as _);
}
pub unsafe fn #gv(#thisparam) -> u32 {
return ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version,
#interface_ident as _);
}
#add_l_stub
#simple_desctr
#(#stubs)*
}
}
fn generate_code<'a, T: std::clone::Clone + Iterator<Item = &'a Protocol>>(
protocols: T,
) -> TokenStream {
let modules = protocols.clone().flat_map(|protocol| {
let pname = Ident::new(&protocol.name, Span::call_site());
let interfaces_code = protocol.interfaces.iter().map(move |iface| {
let doc_attr = iface.description.as_ref().map(description_to_doc_attr);
let mod_name = Ident::new(&iface.name, Span::call_site());
let iface_name = Ident::new(&iface.name, Span::call_site());
let enums = &iface.enums;
let event_handler_trait = gen_event_handler_trait(&iface_name, &iface.events, Side::Client);
let interface = generate_interface(&iface);
let stubs = generate_stubs(&iface, Side::Client);
quote! {
#doc_attr
pub mod #mod_name {
use std::os::raw::{c_char, c_void, c_int};
use super::super::{types_null, NULLPTR};
use super::super::super::sys::common::{wl_interface, wl_array, wl_argument, wl_message, wl_fixed_t};
use super::super::super::sys::client::*;
pub enum #iface_name {}
#(#enums)*
#interface
#event_handler_trait
#stubs
}
}
});
if protocol.name == "wayland" {
quote! {
pub mod #pname {
#(#interfaces_code)*
}
}
}
else {
let mut generic_stable_protocols = vec!["xdg_shell", "org_kde_kwin_outputdevice", "kde_output_device_v2"];
generic_stable_protocols.retain(|i| *i!=protocol.name);
let generic_includes = generic_stable_protocols.iter().map(|name| {let iname = Ident::new(name, Span::call_site()); quote!(use super::#iname::*;)});
quote!{
pub mod #pname {
use super::wayland::*;
#(#generic_includes)*
#(#interfaces_code)*
}
}
}
});
let c_prefix = generate_interfaces_prefix(protocols);
quote! {
#c_prefix
#(#modules)*
}
}
use std::fs::*;
use std::path::PathBuf;
fn get_protocol_files<T: AsRef<Path>>(path: T) -> Vec<PathBuf> {
let mut result: Vec<PathBuf> = vec![];
for i in read_dir(path).unwrap() {
let e = i.unwrap();
let meta = e.metadata().unwrap();
if meta.is_dir() {
result.append(&mut get_protocol_files(e.path()));
} else if meta.is_file()
&& e.path().extension().is_some()
&& e.path().extension().unwrap() == "xml"
{
result.push(e.path().clone());
}
}
return result;
}
fn get_protocols<T: AsRef<Path>>(path: T) -> Vec<Protocol> {
get_protocol_files(path)
.iter()
.flat_map(|f| {
println!("cargo:rerun-if-changed={}", f.as_path().display());
std::panic::catch_unwind(|| load_xml(f)).ok()
})
.fold(std::collections::HashMap::<String, Protocol>::new(), |mut pmap, protocol| {
if pmap.get(&protocol.name).is_none() {
pmap.insert(protocol.name.clone(), protocol.clone());
}
pmap
})
.values()
.cloned()
.collect()
}
fn main() {
let protocols = get_protocols("./protocols");
let out_dir_str = var("OUT_DIR").unwrap();
let out_dir = Path::new(&out_dir_str);
let target = out_dir.join("client.rs");
let mut out = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(&target)
.unwrap();
let code = generate_code(protocols.iter());
write!(&mut out, "{}", code).unwrap();
let _ = Command::new("rustfmt").arg(&target).status();
}