Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate wrapper objects for extension resources #629

Merged
merged 9 commits into from
Jul 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 11 additions & 78 deletions generator/src/generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod output;
mod error_events;
mod namespace;
mod requests_replies;
mod resources;
mod special_cases;

use output::Output;
Expand Down Expand Up @@ -51,11 +52,15 @@ pub(crate) fn generate(module: &xcbgen::defs::Module) -> HashMap<PathBuf, String
let mut enum_cases = HashMap::new();
for ns in module.sorted_namespaces() {
let mut ns_out = Output::new();
let wrapper_info = match ns.ext_info {
None => &XPROTO_RESOURCES[..],
Some(_) => &[],
};
namespace::generate(&ns, &caches, &mut ns_out, &mut enum_cases, wrapper_info);
let wrapper_info = resources::for_extension(&ns.header);
namespace::generate(
module,
&ns,
&caches,
&mut ns_out,
&mut enum_cases,
wrapper_info,
);
out_map.insert(
PathBuf::from(format!("{}.rs", ns.header)),
ns_out.into_data(),
Expand Down Expand Up @@ -223,78 +228,6 @@ struct CreateInfo<'a> {

struct ResourceInfo<'a> {
resource_name: &'a str,
create_requests: [Option<CreateInfo<'a>>; 2],
create_requests: &'a [CreateInfo<'a>],
free_request: &'a str,
}

const XPROTO_RESOURCES: [ResourceInfo<'static>; 6] = [
ResourceInfo {
resource_name: "Pixmap",
create_requests: [
Some(CreateInfo {
request_name: "CreatePixmap",
created_argument: "pid",
}),
None,
],
free_request: "FreePixmap",
},
ResourceInfo {
resource_name: "Window",
create_requests: [
Some(CreateInfo {
request_name: "CreateWindow",
created_argument: "wid",
}),
None,
],
free_request: "DestroyWindow",
},
ResourceInfo {
resource_name: "Font",
create_requests: [
Some(CreateInfo {
request_name: "OpenFont",
created_argument: "fid",
}),
None,
],
free_request: "CloseFont",
},
ResourceInfo {
resource_name: "Gcontext",
create_requests: [
Some(CreateInfo {
request_name: "CreateGC",
created_argument: "cid",
}),
None,
],
free_request: "FreeGC",
},
ResourceInfo {
resource_name: "Colormap",
create_requests: [
Some(CreateInfo {
request_name: "CreateColormap",
created_argument: "mid",
}),
None,
],
free_request: "FreeColormap",
},
ResourceInfo {
resource_name: "Cursor",
create_requests: [
Some(CreateInfo {
request_name: "CreateCursor",
created_argument: "cid",
}),
Some(CreateInfo {
request_name: "CreateGlyphCursor",
created_argument: "cid",
}),
],
free_request: "FreeCursor",
},
];
11 changes: 9 additions & 2 deletions generator/src/generator/namespace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,18 @@ use helpers::{

/// Generate a Rust module for namespace `ns`.
pub(super) fn generate(
module: &xcbgen::defs::Module,
ns: &xcbdefs::Namespace,
caches: &RefCell<Caches>,
out: &mut Output,
enum_cases: &mut EnumCases,
resource_info: &[super::ResourceInfo<'_>],
) {
NamespaceGenerator::new(ns, caches).generate(out, enum_cases, resource_info);
NamespaceGenerator::new(module, ns, caches).generate(out, enum_cases, resource_info);
}

struct NamespaceGenerator<'ns, 'c> {
module: &'ns xcbgen::defs::Module,
ns: &'ns xcbdefs::Namespace,
caches: &'c RefCell<Caches>,

Expand All @@ -50,13 +52,18 @@ struct NamespaceGenerator<'ns, 'c> {

impl<'ns, 'c> NamespaceGenerator<'ns, 'c> {
#[inline]
fn new(ns: &'ns xcbdefs::Namespace, caches: &'c RefCell<Caches>) -> Self {
fn new(
module: &'ns xcbgen::defs::Module,
ns: &'ns xcbdefs::Namespace,
caches: &'c RefCell<Caches>,
) -> Self {
let option_name = if ns.header == "present" {
"std::option::Option"
} else {
"Option"
};
NamespaceGenerator {
module,
ns,
caches,
option_name,
Expand Down
102 changes: 89 additions & 13 deletions generator/src/generator/namespace/resource_wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::collections::BTreeSet;
use std::rc::Rc;

use xcbgen::defs as xcbdefs;

use super::super::{camel_case_to_lower_snake, CreateInfo, ResourceInfo};
use super::super::{camel_case_to_lower_snake, ext_has_feature, CreateInfo, ResourceInfo};
use super::{
gather_deducible_fields, to_rust_type_name, to_rust_variable_name, NamespaceGenerator, Output,
};
Expand Down Expand Up @@ -92,21 +93,30 @@ pub(super) fn generate(
outln!(out, "}}");
outln!(out, "");

let mut uses = BTreeSet::new();

outln!(out, "impl<'c, C: X11Connection> {}<'c, C>", wrapper);
outln!(out, "{{");
out.indented(|out| {
for create_request in info.create_requests.iter().flatten() {
for create_request in info.create_requests.iter() {
generate_creator(
generator,
out,
create_request,
info.resource_name,
&wrapper,
&lower_name,
&mut uses,
);
}
});
outln!(out, "}}");
// Add "use"s for other extensions that appear in the wrapper type
for header in uses.iter() {
outln!(out, "#[cfg(feature = \"{}\")]", header);
outln!(out, "#[allow(unused_imports)]");
outln!(out, "use super::{};", header);
}
outln!(out, "");
outln!(
out,
Expand All @@ -128,7 +138,7 @@ pub(super) fn generate(
);
out.indented(|out| {
outln!(out, "fn drop(&mut self) {{");
outln!(out.indent(), "let _ = (self.0).{}(self.1);", free_function);
outln!(out.indent(), "let _ = {}(self.0, self.1);", free_function);
outln!(out, "}}");
});
outln!(out, "}}");
Expand All @@ -141,11 +151,27 @@ fn generate_creator(
resource_name: &str,
wrapper_name: &str,
lower_name: &str,
uses: &mut BTreeSet<String>,
) {
let request_name = request_info.request_name;
let (request_ext, request_name) = match split_once(request_info.request_name, ':') {
None => (None, request_info.request_name),
Some((ext_name, request_name)) => (Some(ext_name), request_name),
};
let request_ns = request_ext.map(|ext_name| {
generator.module.namespace(ext_name).unwrap_or_else(|| {
panic!(
"Could not find namespace {} for resolving request name {}",
ext_name, request_info.request_name,
);
})
});
let has_feature = request_ns
.as_ref()
.map(|ns| ext_has_feature(&ns.header))
.unwrap_or(false);
let request_ns = request_ns.as_deref().unwrap_or(generator.ns);
let request_def = Rc::clone(
generator
.ns
request_ns
.request_defs
.borrow()
.get(request_name)
Expand All @@ -159,9 +185,20 @@ fn generate_creator(
let request_fields = request_def.fields.borrow();
let deducible_fields = gather_deducible_fields(&*request_fields);

if request_ext.is_some() {
uses.insert(request_ns.header.clone());
}

let mut letter_iter = b'A'..=b'Z';

let function_name = camel_case_to_lower_snake(&request_def.name);
let function_raw_name = camel_case_to_lower_snake(&request_def.name);
let (function_raw_name, function_name) = match request_ext {
Some(_) => (
format!("{}_{}", request_ns.header, function_raw_name),
format!("super::{}::{}", request_ns.header, function_raw_name),
),
None => (function_raw_name.clone(), function_raw_name),
};
let mut function_args = "conn: &'c C".to_string();
let mut forward_args_with_resource = Vec::new();
let mut forward_args_without_resource = Vec::new();
Expand All @@ -181,7 +218,13 @@ fn generate_creator(
xcbdefs::FieldDef::Pad(_) => unimplemented!(),
xcbdefs::FieldDef::Expr(_) => unimplemented!(),
xcbdefs::FieldDef::VirtualLen(_) => unimplemented!(),
xcbdefs::FieldDef::Fd(_) => unimplemented!(),
xcbdefs::FieldDef::Fd(fd_field) => {
let generic_param = format!("{}", char::from(letter_iter.next().unwrap()));
let where_ = format!("{}: Into<RawFdContainer>", generic_param);
generics.push(generic_param.clone());
wheres.push(where_);
(fd_field.name.clone(), generic_param)
}
xcbdefs::FieldDef::FdList(_) => unimplemented!(),
xcbdefs::FieldDef::Normal(normal_field) => {
let rust_field_name = to_rust_variable_name(&normal_field.name);
Expand Down Expand Up @@ -211,7 +254,6 @@ fn generate_creator(
}
xcbdefs::FieldDef::List(list_field) => {
let field_name = to_rust_variable_name(&list_field.name);
assert!(generator.rust_value_type_is_u8(&list_field.element_type));
let element_type =
generator.field_value_type_to_rust_type(&list_field.element_type);
let field_type = if let Some(list_len) = list_field.length() {
Expand Down Expand Up @@ -274,10 +316,13 @@ fn generate_creator(
"/// Errors can come from the call to [X11Connection::generate_id] or [{}].",
function_name,
);
if has_feature {
outln!(out, "#[cfg(feature = \"{}\")]", request_ns.header);
}
outln!(
out,
"pub fn {}_and_get_cookie{}({}) -> Result<(Self, VoidCookie<'c, C>), ReplyOrIdError>",
function_name,
function_raw_name,
generics_decl,
function_args,
);
Expand All @@ -290,7 +335,7 @@ fn generate_creator(
);
outln!(
out.indent(),
"let cookie = conn.{}({})?;",
"let cookie = {}(conn, {})?;",
function_name,
forward_args_with_resource.join(", "),
);
Expand Down Expand Up @@ -328,10 +373,13 @@ fn generate_creator(
"/// Errors can come from the call to [X11Connection::generate_id] or [{}].",
function_name,
);
if has_feature {
outln!(out, "#[cfg(feature = \"{}\")]", request_ns.header);
}
outln!(
out,
"pub fn {}{}({}) -> Result<Self, ReplyOrIdError>",
function_name,
function_raw_name,
generics_decl,
function_args,
);
Expand All @@ -340,7 +388,7 @@ fn generate_creator(
outln!(
out.indent(),
"Ok(Self::{}_and_get_cookie(conn, {})?.0)",
function_name,
function_raw_name,
forward_args_without_resource.join(", "),
);
outln!(out, "}}");
Expand All @@ -354,3 +402,31 @@ fn emit_where(out: &mut Output, wheres: &[String]) {
}
}
}

// This is a poor man's reimplementation of str::split_once().
// TODO: Get rid of this once our MSRV reaches 1.52.0
fn split_once(string: &str, pattern: char) -> Option<(&str, &str)> {
string
.find(pattern)
.map(|index| (&string[..index], &string[(index + pattern.len_utf8())..]))
}

#[cfg(test)]
mod test {
use super::split_once;

#[test]
fn split_once_no_match() {
assert_eq!(None, split_once("abcdef", 'z'));
assert_eq!(None, split_once("äöü€æ@", 'ß'));
assert_eq!(None, split_once("abcdef", '𝄞'));
}

#[test]
fn split_once_match() {
assert_eq!(Some(("abc", "ef")), split_once("abcdef", 'd'));
assert_eq!(Some(("", "aa")), split_once("aaa", 'a'));
assert_eq!(Some(("a", "c")), split_once("aäc", 'ä'));
assert_eq!(Some(("abü", "äö")), split_once("abü𝄞äö", '𝄞'));
}
}
Loading