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(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")?, 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(f: &mut W, field: &Field) -> Result<()> { write!(f, " pub {}: ", field.name)?; write_type(f, &field.ty.val)?; writeln!(f, ",")?; Ok(()) } fn write_message_type( 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(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 {{", 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( 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( f: &mut W, camel_obj_name: &str, messages: &[Lined], 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) -> 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,")?; 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 = 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( f: &mut W, file: &DirEntry, interface_names: &mut Vec, ) -> 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, " #![allow(dead_code)]")?; 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 = None;" )?; } writeln!(f, "}}")?; Ok(()) }