1
0
Fork 0
forked from wry/wry
wry/xml-to-wire/src/builder.rs

134 lines
5.3 KiB
Rust

use {
crate::{
ast::{ArgType, Message, MessageType},
parser::{ParserError, parse},
},
std::{
fs::File,
io::{self, BufWriter, Write},
mem,
},
thiserror::Error,
};
#[derive(Debug, Error)]
pub enum BuilderError {
#[error("Could not process {0}")]
File(String, #[source] Box<BuilderError>),
#[error("Could not read file")]
ReadFile(#[source] io::Error),
#[error("Could not parse file")]
ParseFile(#[source] ParserError),
#[error("Could not format file")]
FormatFile(#[source] io::Error),
#[error("Could not open {0} for writing")]
OpenFile(String, #[source] io::Error),
}
pub fn handle_file(path: &str) -> Result<(), BuilderError> {
handle_file_(path).map_err(|e| BuilderError::File(path.to_string(), Box::new(e)))
}
fn handle_file_(path: &str) -> Result<(), BuilderError> {
let data = std::fs::read(path).map_err(BuilderError::ReadFile)?;
let protocols = parse(&data).map_err(BuilderError::ParseFile)?;
for protocol in protocols {
for i in protocol.interfaces {
let path = format!("wire/{}.txt", i.name);
let mut file =
BufWriter::new(File::create(&path).map_err(|e| BuilderError::OpenFile(path, e))?);
let mut not_first = false;
let mut handle_msg = |msg: &Message| -> Result<(), io::Error> {
if not_first {
writeln!(file)?;
}
not_first = true;
let ty = match msg.request {
true => "request",
false => "event",
};
write!(file, "{ty} {} ", msg.name)?;
if msg.ty.is_some() || msg.since.is_some() {
write!(file, "(")?;
let mut needs_comma = false;
let handle_comma =
|needs_comma: &mut bool, file: &mut BufWriter<File>| -> io::Result<()> {
if mem::take(needs_comma) {
write!(file, ", ")?;
}
Ok(())
};
if let Some(ty) = msg.ty {
match ty {
MessageType::Destructor => {
handle_comma(&mut needs_comma, &mut file)?;
write!(file, "destructor")?;
needs_comma = true;
}
}
}
if let Some(s) = msg.since {
handle_comma(&mut needs_comma, &mut file)?;
write!(file, "since = {}", s)?;
needs_comma = true;
}
let _ = needs_comma;
write!(file, ") ")?;
}
writeln!(file, "{{")?;
let mut args = msg.args.iter().peekable();
while let Some(arg) = args.next() {
if arg.ty == ArgType::Uint
&& arg.enum_.is_none()
&& let Some(prefix) = arg.name.strip_suffix("_hi")
&& let Some(next) = args.peek()
&& next.ty == ArgType::Uint
&& next.enum_.is_none()
&& next.name.strip_prefix(prefix) == Some("_lo")
{
writeln!(file, " {prefix}: u64,")?;
args.next();
continue;
}
if arg.ty == ArgType::NewId && arg.interface.is_none() {
writeln!(file, " interface: str,")?;
writeln!(file, " version: u32,")?;
}
write!(file, " {}: ", arg.name)?;
'ty: {
let ty = match arg.ty {
ArgType::NewId | ArgType::Object => {
write!(
file,
"id({})",
arg.interface.as_deref().unwrap_or("object")
)?;
if arg.ty == ArgType::NewId {
write!(file, " (new)")?;
}
break 'ty;
}
ArgType::Int => "i32",
ArgType::Uint => "u32",
ArgType::Fixed => "fixed",
ArgType::String => match arg.allow_null {
true => "optstr",
false => "str",
},
ArgType::Array => "array(pod(u8))",
ArgType::Fd => "fd",
};
write!(file, "{}", ty)?;
}
writeln!(file, ",")?;
}
writeln!(file, "}}")?;
Ok(())
};
for m in &i.messages {
handle_msg(m).map_err(BuilderError::FormatFile)?;
}
}
}
Ok(())
}