475 lines
16 KiB
Rust
475 lines
16 KiB
Rust
mod parser;
|
|
|
|
use {
|
|
crate::{
|
|
open,
|
|
wire::parser::{Field, Lined, Message, Type, parse_messages, to_camel},
|
|
},
|
|
anyhow::{Context, Result},
|
|
std::{fmt, fs::DirEntry, io::Write, os::unix::ffi::OsStrExt},
|
|
};
|
|
|
|
fn write_type<W: Write>(f: &mut W, ty: &Type) -> Result<()> {
|
|
match ty {
|
|
Type::Id(_, id) => write!(f, "{}Id", id)?,
|
|
Type::U32 => write!(f, "u32")?,
|
|
Type::I32 => write!(f, "i32")?,
|
|
Type::U64 => write!(f, "u64")?,
|
|
Type::U64Rev => write!(f, "u64")?,
|
|
Type::Str => write!(f, "&'a str")?,
|
|
Type::OptStr => write!(f, "Option<&'a str>")?,
|
|
Type::BStr => write!(f, "&'a BStr")?,
|
|
Type::Fixed => write!(f, "Fixed")?,
|
|
Type::Fd => write!(f, "Rc<OwnedFd>")?,
|
|
Type::Array(n) => {
|
|
write!(f, "&'a [")?;
|
|
write_type(f, n)?;
|
|
write!(f, "]")?;
|
|
}
|
|
Type::Pod(p) => f.write_all(p.as_bytes())?,
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn write_field<W: Write>(f: &mut W, field: &Field) -> Result<()> {
|
|
write!(f, " pub {}: ", field.name)?;
|
|
write_type(f, &field.ty.val)?;
|
|
writeln!(f, ",")?;
|
|
Ok(())
|
|
}
|
|
|
|
fn write_message_type<W: Write>(
|
|
f: &mut W,
|
|
obj: &str,
|
|
message: &Message,
|
|
needs_lifetime: bool,
|
|
) -> Result<()> {
|
|
let lifetime = if needs_lifetime { "<'a>" } else { "" };
|
|
writeln!(f, " pub struct {}{} {{", message.camel_name, lifetime)?;
|
|
writeln!(f, " pub self_id: {}Id,", obj)?;
|
|
for field in &message.fields {
|
|
write_field(f, &field.val)?;
|
|
}
|
|
writeln!(f, " }}")?;
|
|
writeln!(
|
|
f,
|
|
" impl{} std::fmt::Debug for {}{} {{",
|
|
lifetime, message.camel_name, lifetime
|
|
)?;
|
|
writeln!(
|
|
f,
|
|
" fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{"
|
|
)?;
|
|
write!(f, r#" write!(fmt, "{}("#, message.name)?;
|
|
for (i, field) in message.fields.iter().enumerate() {
|
|
if i > 0 {
|
|
write!(f, ", ")?;
|
|
}
|
|
let formatter = match &field.val.ty.val {
|
|
Type::OptStr | Type::Str | Type::Fd | Type::Array(..) => "{:?}",
|
|
_ => "{}",
|
|
};
|
|
write!(f, "{}: {}", field.val.name, formatter)?;
|
|
}
|
|
write!(f, r#")""#)?;
|
|
for field in &message.fields {
|
|
write!(f, ", self.{}", field.val.name)?;
|
|
}
|
|
writeln!(f, r")")?;
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " }}")?;
|
|
Ok(())
|
|
}
|
|
|
|
fn write_message<W: Write>(f: &mut W, obj: &str, message: &Message) -> Result<()> {
|
|
let has_reference_type = message.has_reference_type;
|
|
let uppercase = message.name.to_ascii_uppercase();
|
|
writeln!(f)?;
|
|
writeln!(f, " pub const {}: u32 = {};", uppercase, message.id)?;
|
|
write_message_type(f, obj, message, has_reference_type)?;
|
|
let lifetime = if has_reference_type { "<'a>" } else { "" };
|
|
let lifetime_b = if has_reference_type { "<'b>" } else { "" };
|
|
let parser = if message.fields.len() > 0 {
|
|
"parser"
|
|
} else {
|
|
"_parser"
|
|
};
|
|
writeln!(
|
|
f,
|
|
" impl<'a> RequestParser<'a> for {}{} {{",
|
|
message.camel_name, lifetime
|
|
)?;
|
|
writeln!(
|
|
f,
|
|
" type Generic<'b> = {}{};",
|
|
message.camel_name, lifetime_b,
|
|
)?;
|
|
writeln!(f, " const ID: u32 = {};", message.id)?;
|
|
writeln!(
|
|
f,
|
|
" fn parse({}: &mut MsgParser<'_, 'a>) -> Result<Self, MsgParserError> {{",
|
|
parser
|
|
)?;
|
|
if message.is_fixed_size {
|
|
writeln!(f, " let [")?;
|
|
for (i, field) in message.fields.iter().enumerate() {
|
|
match &field.val.ty.val {
|
|
Type::U64 => {
|
|
writeln!(f, " arg{i}_hi,")?;
|
|
writeln!(f, " arg{i}_lo,")?;
|
|
}
|
|
Type::U64Rev => {
|
|
writeln!(f, " arg{i}_lo,")?;
|
|
writeln!(f, " arg{i}_hi,")?;
|
|
}
|
|
Type::Fd => {}
|
|
_ => {
|
|
writeln!(f, " arg{i},")?;
|
|
}
|
|
}
|
|
}
|
|
writeln!(f, " ] = *{parser}.data() else {{")?;
|
|
writeln!(
|
|
f,
|
|
" return Err(MsgParserError::UnexpectedMessageSize);"
|
|
)?;
|
|
writeln!(f, " }};")?;
|
|
writeln!(f, " Ok(Self {{")?;
|
|
writeln!(f, " self_id: {}Id::NONE,", obj)?;
|
|
for (i, field) in message.fields.iter().enumerate() {
|
|
writeln!(
|
|
f,
|
|
" {}: {},",
|
|
field.val.name,
|
|
fmt::from_fn(|f| {
|
|
match &field.val.ty.val {
|
|
Type::Id(_, name) => write!(f, "{name}Id(arg{i})"),
|
|
Type::U32 => write!(f, "arg{i}"),
|
|
Type::I32 => write!(f, "arg{i} as i32"),
|
|
Type::U64 | Type::U64Rev => {
|
|
write!(f, "((arg{i}_hi as u64) << 32) | (arg{i}_lo as u64)")
|
|
}
|
|
Type::OptStr => unreachable!(),
|
|
Type::Str => unreachable!(),
|
|
Type::Fixed => write!(f, "Fixed(arg{i} as i32)"),
|
|
Type::Fd => write!(f, "parser.fd()?"),
|
|
Type::BStr => unreachable!(),
|
|
Type::Array(_) => unreachable!(),
|
|
Type::Pod(_) => unreachable!(),
|
|
}
|
|
})
|
|
)?;
|
|
}
|
|
writeln!(f, " }})")?;
|
|
} else {
|
|
writeln!(f, " let res = Ok(Self {{")?;
|
|
writeln!(f, " self_id: {}Id::NONE,", obj)?;
|
|
for field in &message.fields {
|
|
let p = match &field.val.ty.val {
|
|
Type::Id(..) => "object",
|
|
Type::U32 => "uint",
|
|
Type::I32 => "int",
|
|
Type::U64 => "u64",
|
|
Type::U64Rev => "u64_rev",
|
|
Type::OptStr => "optstr",
|
|
Type::Str => "str",
|
|
Type::Fixed => "fixed",
|
|
Type::Fd => "fd",
|
|
Type::BStr => "bstr",
|
|
Type::Array(_) => "binary_array",
|
|
Type::Pod(_) => "binary",
|
|
};
|
|
writeln!(f, " {}: parser.{}()?,", field.val.name, p)?;
|
|
}
|
|
writeln!(f, " }});")?;
|
|
writeln!(f, " parser.eof()?;")?;
|
|
writeln!(f, " res")?;
|
|
}
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " }}")?;
|
|
writeln!(
|
|
f,
|
|
" impl{} EventFormatter for {}{} {{",
|
|
lifetime, message.camel_name, lifetime
|
|
)?;
|
|
writeln!(f, " fn format(self, fmt: &mut MsgFormatter<'_>) {{")?;
|
|
if message.is_fixed_size {
|
|
writeln!(f, " fmt.data(&[")?;
|
|
writeln!(f, " self.self_id.0,")?;
|
|
writeln!(f, " {uppercase},")?;
|
|
for field in &message.fields {
|
|
let prefix = format!(" self.{}", field.val.name);
|
|
match &field.val.ty.val {
|
|
Type::Id(_, _) => writeln!(f, "{prefix}.0,")?,
|
|
Type::U32 => writeln!(f, "{prefix},")?,
|
|
Type::I32 => writeln!(f, "{prefix} as u32,")?,
|
|
Type::U64 => {
|
|
writeln!(f, " (self.{} >> 32) as u32,", field.val.name)?;
|
|
writeln!(f, "{prefix} as u32,")?;
|
|
}
|
|
Type::U64Rev => {
|
|
writeln!(f, "{prefix} as u32,")?;
|
|
writeln!(f, " (self.{} >> 32) as u32,", field.val.name)?;
|
|
}
|
|
Type::Str => unreachable!(),
|
|
Type::OptStr => unreachable!(),
|
|
Type::BStr => unreachable!(),
|
|
Type::Fixed => writeln!(f, "{prefix}.0 as u32,")?,
|
|
Type::Fd => {}
|
|
Type::Array(_) => unreachable!(),
|
|
Type::Pod(_) => unreachable!(),
|
|
}
|
|
}
|
|
writeln!(f, " ]);")?;
|
|
for field in &message.fields {
|
|
if let Type::Fd = &field.val.ty.val {
|
|
writeln!(f, " fmt.fd(self.{});", field.val.name)?;
|
|
}
|
|
}
|
|
} else {
|
|
writeln!(f, " fmt.header(self.self_id, {});", uppercase)?;
|
|
fn write_fmt_expr<W: Write>(
|
|
f: &mut W,
|
|
prefix: &str,
|
|
ty: &Type,
|
|
access: &str,
|
|
) -> Result<()> {
|
|
let p = match ty {
|
|
Type::Id(..) => "object",
|
|
Type::U32 => "uint",
|
|
Type::I32 => "int",
|
|
Type::U64 => "u64",
|
|
Type::U64Rev => "u64_rev",
|
|
Type::OptStr => "optstr",
|
|
Type::Str | Type::BStr => "string",
|
|
Type::Fixed => "fixed",
|
|
Type::Fd => "fd",
|
|
Type::Array(..) => "binary",
|
|
Type::Pod(..) => "binary",
|
|
};
|
|
let rf = match ty {
|
|
Type::Pod(..) => "&",
|
|
_ => "",
|
|
};
|
|
writeln!(f, " {}fmt.{}({}{});", prefix, p, rf, access)?;
|
|
Ok(())
|
|
}
|
|
for field in &message.fields {
|
|
write_fmt_expr(
|
|
f,
|
|
"",
|
|
&field.val.ty.val,
|
|
&format!("self.{}", field.val.name),
|
|
)?;
|
|
}
|
|
}
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " fn id(&self) -> ObjectId {{")?;
|
|
writeln!(f, " self.self_id.into()")?;
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " fn interface(&self) -> Interface {{")?;
|
|
writeln!(f, " {}", obj)?;
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " }}")?;
|
|
Ok(())
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
enum RequestHandlerDirection {
|
|
Request,
|
|
Event,
|
|
}
|
|
|
|
fn write_request_handler<W: Write>(
|
|
f: &mut W,
|
|
camel_obj_name: &str,
|
|
messages: &[Lined<Message>],
|
|
direction: RequestHandlerDirection,
|
|
) -> Result<()> {
|
|
let snake_direction;
|
|
let camel_direction;
|
|
let parent;
|
|
let parser;
|
|
let error;
|
|
let param;
|
|
writeln!(f)?;
|
|
match direction {
|
|
RequestHandlerDirection::Request => {
|
|
snake_direction = "request";
|
|
camel_direction = "Request";
|
|
parent = "crate::object::Object";
|
|
parser = "crate::client::Client";
|
|
error = "crate::client::ClientError";
|
|
param = "req";
|
|
}
|
|
RequestHandlerDirection::Event => {
|
|
snake_direction = "event";
|
|
camel_direction = "Event";
|
|
parent = "crate::wl_usr::usr_object::UsrObject";
|
|
parser = "crate::wl_usr::UsrCon";
|
|
error = "crate::wl_usr::UsrConError";
|
|
param = "ev";
|
|
writeln!(f, " #[allow(clippy::allow_attributes, dead_code)]")?;
|
|
}
|
|
}
|
|
writeln!(
|
|
f,
|
|
" pub trait {camel_obj_name}{camel_direction}Handler: {parent} + Sized {{"
|
|
)?;
|
|
writeln!(f, " type Error: std::error::Error;")?;
|
|
for message in messages {
|
|
let msg = &message.val;
|
|
let lt = match msg.has_reference_type {
|
|
true => "<'_>",
|
|
false => "",
|
|
};
|
|
writeln!(f)?;
|
|
writeln!(
|
|
f,
|
|
" fn {}(&self, {param}: {}{lt}, _slf: &Rc<Self>) -> Result<(), Self::Error>;",
|
|
msg.safe_name, msg.camel_name
|
|
)?;
|
|
}
|
|
writeln!(f)?;
|
|
writeln!(f, " #[inline(always)]")?;
|
|
writeln!(f, " fn handle_{snake_direction}_impl(")?;
|
|
writeln!(f, " self: Rc<Self>,")?;
|
|
writeln!(f, " client: &{parser},")?;
|
|
writeln!(f, " req: u32,")?;
|
|
writeln!(
|
|
f,
|
|
" parser: crate::utils::buffd::MsgParser<'_, '_>,"
|
|
)?;
|
|
writeln!(f, " ) -> Result<(), {error}> {{")?;
|
|
if messages.is_empty() {
|
|
writeln!(f, " #![allow(unused_variables)]")?;
|
|
writeln!(f, " Err({error}::InvalidMethod)")?;
|
|
} else {
|
|
writeln!(f, " let method;")?;
|
|
writeln!(
|
|
f,
|
|
" let error: Box<dyn std::error::Error> = match req {{"
|
|
)?;
|
|
for message in messages {
|
|
let msg = &message.val;
|
|
write!(f, " {} ", msg.id)?;
|
|
if let Some(since) = msg.attribs.since {
|
|
write!(f, "if self.version() >= {since} ")?;
|
|
}
|
|
writeln!(f, "=> {{")?;
|
|
writeln!(f, " method = \"{}\";", msg.name)?;
|
|
writeln!(
|
|
f,
|
|
" match client.parse(&*self, parser) {{"
|
|
)?;
|
|
writeln!(
|
|
f,
|
|
" Ok(req) => match self.{}(req, &self) {{",
|
|
msg.safe_name
|
|
)?;
|
|
writeln!(f, " Ok(()) => return Ok(()),")?;
|
|
writeln!(f, " Err(e) => Box::new(e),")?;
|
|
writeln!(f, " }},")?;
|
|
writeln!(
|
|
f,
|
|
" Err(e) => Box::new(crate::client::ParserError(e)),"
|
|
)?;
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " }},")?;
|
|
}
|
|
writeln!(
|
|
f,
|
|
" _ => return Err({error}::InvalidMethod),"
|
|
)?;
|
|
writeln!(f, " }};")?;
|
|
writeln!(f, " Err({error}::MethodError {{")?;
|
|
writeln!(f, " interface: {camel_obj_name},")?;
|
|
writeln!(f, " id: self.id(),")?;
|
|
writeln!(f, " method,")?;
|
|
writeln!(f, " error,")?;
|
|
writeln!(f, " }})")?;
|
|
}
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " }}")?;
|
|
Ok(())
|
|
}
|
|
|
|
fn write_file<W: Write>(
|
|
f: &mut W,
|
|
file: &DirEntry,
|
|
interface_names: &mut Vec<String>,
|
|
) -> Result<()> {
|
|
let file_name = file.file_name();
|
|
let file_name = std::str::from_utf8(file_name.as_bytes())?;
|
|
println!("cargo:rerun-if-changed=wire/{}", file_name);
|
|
let obj_name = file_name.split(".").next().unwrap();
|
|
let camel_obj_name = to_camel(obj_name);
|
|
interface_names.push(camel_obj_name.clone());
|
|
writeln!(f)?;
|
|
writeln!(f, "id!({}Id);", camel_obj_name)?;
|
|
writeln!(f)?;
|
|
writeln!(
|
|
f,
|
|
"pub const {}: Interface = Interface(\"{}\");",
|
|
camel_obj_name, obj_name
|
|
)?;
|
|
let contents = std::fs::read(file.path())?;
|
|
let messages = parse_messages(&contents)?;
|
|
writeln!(f)?;
|
|
writeln!(f, "pub mod {} {{", obj_name)?;
|
|
writeln!(f, " use super::*;")?;
|
|
for message in messages.requests.iter().chain(messages.events.iter()) {
|
|
write_message(f, &camel_obj_name, &message.val)?;
|
|
}
|
|
write_request_handler(
|
|
f,
|
|
&camel_obj_name,
|
|
&messages.requests,
|
|
RequestHandlerDirection::Request,
|
|
)?;
|
|
write_request_handler(
|
|
f,
|
|
&camel_obj_name,
|
|
&messages.events,
|
|
RequestHandlerDirection::Event,
|
|
)?;
|
|
writeln!(f, "}}")?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn main() -> Result<()> {
|
|
let mut f = open("wire.rs")?;
|
|
writeln!(f, "use std::rc::Rc;")?;
|
|
writeln!(f, "use uapi::OwnedFd;")?;
|
|
writeln!(f, "use bstr::BStr;")?;
|
|
writeln!(f, "use crate::fixed::Fixed;")?;
|
|
writeln!(f, "use crate::client::{{EventFormatter, RequestParser}};")?;
|
|
writeln!(f, "use crate::object::{{ObjectId, Interface}};")?;
|
|
writeln!(
|
|
f,
|
|
"use crate::utils::buffd::{{MsgFormatter, MsgParser, MsgParserError}};"
|
|
)?;
|
|
println!("cargo:rerun-if-changed=wire");
|
|
let mut files = vec![];
|
|
for file in std::fs::read_dir("wire")? {
|
|
files.push(file?);
|
|
}
|
|
files.sort_by_key(|f| f.file_name());
|
|
let mut interface_names = vec![];
|
|
for file in files {
|
|
write_file(&mut f, &file, &mut interface_names)
|
|
.with_context(|| format!("While processing {}", file.path().display()))?;
|
|
}
|
|
writeln!(f)?;
|
|
writeln!(f, "#[doc(hidden)]")?;
|
|
writeln!(f, "#[expect(dead_code)]")?;
|
|
writeln!(f, "pub mod interface_singletons {{")?;
|
|
for interface in &interface_names {
|
|
writeln!(
|
|
f,
|
|
" pub const {interface}: Option<crate::globals::Singleton> = None;"
|
|
)?;
|
|
}
|
|
writeln!(f, "}}")?;
|
|
Ok(())
|
|
}
|