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

Send requests via traits and clean up generated code #671

Merged
merged 4 commits into from
Mar 20, 2022
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
23 changes: 20 additions & 3 deletions generator/src/generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,32 @@ pub(crate) fn generate(module: &xcbgen::defs::Module) -> Vec<Generated> {
outln!(main_proto_out, "use crate::utils::RawFdContainer;");
outln!(
main_proto_out,
"use crate::x11_utils::{{TryParse, X11Error}};"
"use crate::x11_utils::{{TryParse, TryParseFd, X11Error, ReplyRequest, ReplyFDsRequest}};"
);
outln!(
main_proto_out,
"use crate::x11_utils::{{ExtInfoProvider, ReplyParsingFunction, Request as RequestTrait, \
RequestHeader}};"
"use crate::x11_utils::{{ExtInfoProvider, ReplyParsingFunction, RequestHeader}};"
);
outln!(main_proto_out, "");

outln!(main_proto_out, "fn parse_reply<'a, R: ReplyRequest>(bytes: &'a [u8], _: &mut Vec<RawFdContainer>) -> Result<(Reply, &'a [u8]), ParseError> {{");
main_proto_out.indented(|out| {
outln!(out, "let (reply, remaining) = R::Reply::try_parse(bytes)?;");
outln!(out, "Ok((reply.into(), remaining))");
});
outln!(main_proto_out, "}}");
outln!(main_proto_out, "#[allow(dead_code)]");
outln!(main_proto_out, "fn parse_reply_fds<'a, R: ReplyFDsRequest>(bytes: &'a [u8], fds: &mut Vec<RawFdContainer>) -> Result<(Reply, &'a [u8]), ParseError> {{");
main_proto_out.indented(|out| {
outln!(
out,
"let (reply, remaining) = R::Reply::try_parse_fd(bytes, fds)?;"
);
outln!(out, "Ok((reply.into(), remaining))");
});
outln!(main_proto_out, "}}");
outln!(main_proto_out, "");

let caches = RefCell::new(namespace::helpers::Caches::default());
caches.borrow_mut().gather_enum_infos(module);

Expand Down
132 changes: 46 additions & 86 deletions generator/src/generator/namespace/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ pub(super) fn generate_request(
&deducible_fields,
&gathered,
proto_out,
x11rb_out,
);
let ns_prefix = get_ns_name_prefix(generator.ns);
let lifetime_block = if gathered.needs_lifetime {
Expand Down Expand Up @@ -243,10 +242,15 @@ pub(super) fn generate_request(
header = generator.ns.header,
));
enum_cases.reply_parse_cases.push(format!(
"Request::{ns_prefix}{name}(_) => Some({header}::{name}Request::parse_reply),",
"Request::{ns_prefix}{name}(_) => Some({func}::<{header}::{name}Request>),",
ns_prefix = ns_prefix,
name = name,
header = generator.ns.header,
func = if gathered.reply_has_fds {
"parse_reply_fds"
} else {
"parse_reply"
},
));
enum_cases.reply_from_cases.push(format!(
r#"impl From<{header}::{name}Reply> for Reply {{
Expand Down Expand Up @@ -384,7 +388,6 @@ fn emit_request_struct(
deducible_fields: &HashMap<String, DeducibleField>,
gathered: &GatheredRequestFields,
out: &mut Output,
x11rb_out: &mut Output,
) {
let ns = request_def.namespace.upgrade().unwrap();
let is_xproto = ns.header == "xproto";
Expand Down Expand Up @@ -868,69 +871,6 @@ fn emit_request_struct(
});
outln!(out, "}}");

// Sending method
let is_xproto = ns.header == "xproto";
let is_list_fonts_with_info = request_def.name == "ListFontsWithInfo" && is_xproto;
let is_record_enable_context = request_def.name == "EnableContext" && ns.header == "record";
let ret_type = if is_list_fonts_with_info || is_record_enable_context {
assert!(request_def.reply.is_some());
match (is_list_fonts_with_info, is_record_enable_context) {
(true, _) => "ListFontsWithInfoCookie<'c, Conn>".to_string(),
(_, true) => "RecordEnableContextCookie<'c, Conn>".to_string(),
_ => unreachable!(),
}
} else {
match (request_def.reply.is_some(), gathered.reply_has_fds) {
(false, _) => "VoidCookie<'c, Conn>".to_string(),
(true, false) => format!("Cookie<'c, Conn, {}Reply>", name),
(true, true) => format!("CookieWithFds<'c, Conn, {}Reply>", name),
}
};
outln!(
x11rb_out,
"fn send_{lower_name}<'c, Conn>(req: {name}Request{lifetime}, conn: &'c Conn) -> Result<{ret_type}, ConnectionError>",
ret_type=ret_type,
name=name,
lower_name=super::super::camel_case_to_lower_snake(name),
lifetime=if gathered.needs_lifetime { "<'_>" } else { "" },
);
outln!(x11rb_out, "where");
outln!(x11rb_out.indent(), "Conn: RequestConnection + ?Sized,");
outln!(x11rb_out, "{{");
x11rb_out.indented(|x11rb_out| {
outln!(
x11rb_out,
"let (bytes, fds) = req.serialize({});",
if is_xproto { "" } else { "major_opcode(conn)?" }
);
outln!(
x11rb_out,
"let slices = bytes.iter().map(|b| IoSlice::new(&*b)).collect::<Vec<_>>();"
);

if is_list_fonts_with_info || is_record_enable_context {
let cookie = match (is_list_fonts_with_info, is_record_enable_context) {
(true, _) => "ListFontsWithInfoCookie",
(_, true) => "RecordEnableContextCookie",
_ => unreachable!(),
};
outln!(
x11rb_out,
"Ok({}::new(conn.send_request_with_reply(&slices, fds)?))",
cookie,
)
} else if request_def.reply.is_some() {
if gathered.reply_has_fds {
outln!(x11rb_out, "conn.send_request_with_reply_with_fds(&slices, fds)");
} else {
outln!(x11rb_out, "conn.send_request_with_reply(&slices, fds)",);
}
} else {
outln!(x11rb_out, "conn.send_request_without_reply(&slices, fds)",);
}
});
outln!(x11rb_out, "}}");

// Parsing implementation.
outln!(
out,
Expand Down Expand Up @@ -1100,13 +1040,6 @@ fn emit_request_struct(
name = name
);
out.indented(|out| {
if request_def.reply.is_some() {
outln!(out, "type Reply = {}Reply;", name);
} else {
outln!(out, "type Reply = ();");
}

outln!(out, "");
if is_xproto {
outln!(out, "const EXTENSION_NAME: Option<&'static str> = None;");
} else {
Expand Down Expand Up @@ -1154,11 +1087,15 @@ fn emit_request_struct(
};
outln!(
out,
"impl{lifetime} {request_trait} for {name}Request{lifetime} {{}}",
"impl{lifetime} {request_trait} for {name}Request{lifetime} {{",
name = name,
request_trait = request_trait,
lifetime = struct_lifetime_block,
);
if request_def.reply.is_some() {
outln!(out.indent(), "type Reply = {}Reply;", name);
};
outln!(out, "}}");
}

fn emit_request_function(
Expand All @@ -1170,8 +1107,9 @@ fn emit_request_function(
out: &mut Output,
) {
let ns = request_def.namespace.upgrade().unwrap();
let is_list_fonts_with_info = request_def.name == "ListFontsWithInfo" && ns.header == "xproto";
let is_send_event = request_def.name == "SendEvent" && ns.header == "xproto";
let is_xproto = ns.header == "xproto";
let is_list_fonts_with_info = request_def.name == "ListFontsWithInfo" && is_xproto;
let is_send_event = request_def.name == "SendEvent" && is_xproto;
let is_record_enable_context = request_def.name == "EnableContext" && ns.header == "record";

let needs_lifetime = gathered.needs_lifetime && !is_send_event;
Expand All @@ -1188,20 +1126,22 @@ fn emit_request_function(
}

let ret_lifetime = if needs_lifetime { "'c" } else { "'_" };
let ret_type = if is_list_fonts_with_info || is_record_enable_context {
let (ret_type, special_cookie) = if is_list_fonts_with_info || is_record_enable_context {
assert!(request_def.reply.is_some());
assert!(!gathered.reply_has_fds);
if is_list_fonts_with_info {
format!("ListFontsWithInfoCookie<{}, Conn>", ret_lifetime)
let cookie = if is_list_fonts_with_info {
"ListFontsWithInfoCookie"
} else {
format!("RecordEnableContextCookie<{}, Conn>", ret_lifetime)
}
"RecordEnableContextCookie"
};
(format!("{}<{}, Conn>", cookie, ret_lifetime), Some(cookie))
} else {
match (request_def.reply.is_some(), gathered.reply_has_fds) {
let ret_type = match (request_def.reply.is_some(), gathered.reply_has_fds) {
(false, _) => format!("VoidCookie<{}, Conn>", ret_lifetime),
(true, false) => format!("Cookie<{}, Conn, {}Reply>", ret_lifetime, name),
(true, true) => format!("CookieWithFds<{}, Conn, {}Reply>", ret_lifetime, name),
}
};
(ret_type, None)
};

let mut args = String::new();
Expand Down Expand Up @@ -1260,9 +1200,29 @@ fn emit_request_function(

outln!(
out,
"send_{}(request0, conn)",
super::super::camel_case_to_lower_snake(name)
)
"let (bytes, fds) = request0.serialize({});",
if is_xproto { "" } else { "major_opcode(conn)?" }
);
outln!(
out,
"let slices = bytes.iter().map(|b| IoSlice::new(&*b)).collect::<Vec<_>>();"
);

if let Some(cookie) = special_cookie {
outln!(
out,
"Ok({}::new(conn.send_request_with_reply(&slices, fds)?))",
cookie,
)
} else if request_def.reply.is_some() {
if gathered.reply_has_fds {
outln!(out, "conn.send_request_with_reply_with_fds(&slices, fds)");
} else {
outln!(out, "conn.send_request_with_reply(&slices, fds)",);
}
} else {
outln!(out, "conn.send_request_without_reply(&slices, fds)",);
}
});
outln!(out, "}}");
}
Expand Down
71 changes: 71 additions & 0 deletions src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use std::borrow::Cow;
use std::convert::{TryFrom, TryInto};
use std::io::IoSlice;

use x11rb_protocol::x11_utils::{ReplyFDsRequest, ReplyRequest, VoidRequest};

use crate::cookie::{Cookie, CookieWithFds, VoidCookie};
use crate::errors::{ConnectionError, ParseError, ReplyError, ReplyOrIdError};
use crate::protocol::xproto::Setup;
Expand Down Expand Up @@ -75,6 +77,29 @@ pub trait RequestConnection {
where
R: TryParse;

/// Send a request with a reply to the server.
///
/// This function is a wrapper around [`send_request_with_reply`]. This function gets a
/// [`ReplyRequest`] as its argument to specify the request to send.
fn send_trait_request_with_reply<R>(
&self,
request: R,
) -> Result<Cookie<'_, Self, <R as ReplyRequest>::Reply>, ConnectionError>
where
R: ReplyRequest,
{
let opcode = match R::EXTENSION_NAME {
None => 0,
Some(extension) => {
self.extension_information(extension)?
.ok_or(ConnectionError::UnsupportedExtension)?
.major_opcode
}
};
let (buf, fds) = request.serialize(opcode);
self.send_request_with_reply(&[IoSlice::new(&*buf)], fds)
}

/// Send a request with a reply containing file descriptors to the server.
///
/// The `bufs` parameter describes the raw bytes that should be sent. The returned cookie
Expand Down Expand Up @@ -102,6 +127,29 @@ pub trait RequestConnection {
where
R: TryParseFd;

/// Send a request with a reply containing file descriptors to the server.
///
/// This function is a wrapper around [`send_request_with_reply_with_fds`]. This function gets
/// a [`ReplyFDsRequest`] as its argument to specify the request to send.
fn send_trait_request_with_reply_with_fds<R>(
&self,
request: R,
) -> Result<CookieWithFds<'_, Self, R::Reply>, ConnectionError>
where
R: ReplyFDsRequest,
{
let opcode = match R::EXTENSION_NAME {
None => 0,
Some(extension) => {
self.extension_information(extension)?
.ok_or(ConnectionError::UnsupportedExtension)?
.major_opcode
}
};
let (buf, fds) = request.serialize(opcode);
self.send_request_with_reply_with_fds(&[IoSlice::new(&*buf)], fds)
}

/// Send a request without a reply to the server.
///
/// The `bufs` parameter describes the raw bytes that should be sent. The sequence number of
Expand All @@ -127,6 +175,29 @@ pub trait RequestConnection {
fds: Vec<RawFdContainer>,
) -> Result<VoidCookie<'_, Self>, ConnectionError>;

/// Send a request without a reply to the server.
///
/// This function is a wrapper around [`send_request_without_reply`]. This function gets a
/// [`VoidRequest`] as its argument to specify the request to send.
fn send_trait_request_without_reply<R>(
&self,
request: R,
) -> Result<VoidCookie<'_, Self>, ConnectionError>
where
R: VoidRequest,
{
let opcode = match R::EXTENSION_NAME {
None => 0,
Some(extension) => {
self.extension_information(extension)?
.ok_or(ConnectionError::UnsupportedExtension)?
.major_opcode
}
};
let (buf, fds) = request.serialize(opcode);
self.send_request_without_reply(&[IoSlice::new(&*buf)], fds)
}

/// A reply to an error should be discarded.
///
/// This method is automatically called by the `Drop` implementation on `Cookie` so that any
Expand Down
12 changes: 3 additions & 9 deletions src/protocol/bigreq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,14 @@ fn major_opcode<Conn: RequestConnection + ?Sized>(conn: &Conn) -> Result<u8, Con
Ok(info.major_opcode)
}

fn send_enable<'c, Conn>(req: EnableRequest, conn: &'c Conn) -> Result<Cookie<'c, Conn, EnableReply>, ConnectionError>
where
Conn: RequestConnection + ?Sized,
{
let (bytes, fds) = req.serialize(major_opcode(conn)?);
let slices = bytes.iter().map(|b| IoSlice::new(&*b)).collect::<Vec<_>>();
conn.send_request_with_reply(&slices, fds)
}
pub fn enable<Conn>(conn: &Conn) -> Result<Cookie<'_, Conn, EnableReply>, ConnectionError>
where
Conn: RequestConnection + ?Sized,
{
let request0 = EnableRequest;
send_enable(request0, conn)
let (bytes, fds) = request0.serialize(major_opcode(conn)?);
let slices = bytes.iter().map(|b| IoSlice::new(&*b)).collect::<Vec<_>>();
conn.send_request_with_reply(&slices, fds)
}

/// Extension trait defining the requests of this extension.
Expand Down
Loading