#protocols #generation #glue #dispatch #proto #yutani

yutani-codegen

Code generation of protocol dispatch glue for Yutani

1 unstable release

0.0.0 Nov 5, 2022

#33 in #glue

MIT license

23KB
487 lines

yutani-codegen

Code generation for yutani.

Wayland specifies protocols in XML (or TOML in the case of yutani) with all of the necessary information to automatically generate dispatch glue so that implementing a Wayland protocol can be as simple as defining the required functions.

Usage

This crate can be used either in a build script or macro. Using a build script reduces the amount of work required, potentially improving compile times, and will also integrate better with Rust Analyzer.

So that the generated code isn't a complete eye-bleed you should clean it up with rustfmt.

An example build.rs script:

use heck::ToKebabCase;
use std::{io::Write, fs::File, process::Command};

const PROTO_DIR: &'static str = "src/wayland/proto";
const PROTOCOLS: &'static [&'static str] = &[
    "wayland",
    "xdg_shell",
    "linux_dmabuf_unstable_v1"
];

fn main() {
    // Generate the proto module to import the generated code
    let mod_path = &format!("{PROTO_DIR}/mod.rs");
    let mut proto_mod = match File::create(mod_path) {
        Ok(proto_mod) => proto_mod,
        Err(error) => panic!("Failed to create Rust source file '{mod_path}': {error:?}")
    };
    if let Err(error) = writeln!(proto_mod, "// Auto-Generated file. Do not edit.") {
        panic!("Failed to write Rust source file '{mod_path}': {error:?}")
    }
    // Generate Wayland dispatch glue
    for protocol in PROTOCOLS {
        if let Err(error) = writeln!(proto_mod, "mod {protocol};\npub use {protocol}::*;") {
            panic!("Failed to write Rust source file '{mod_path}': {error:?}")
        }
        yutani_codegen(protocol)
    }
}

fn yutani_codegen(protocol: &str) {
    let spec = &format!("protocol/{}.toml", protocol.to_kebab_case());
    let proto = &format!("{PROTO_DIR}/{protocol}.rs");

    println!("cargo:rerun-if-changed={spec}");
    println!("cargo:rerun-if-changed={proto}");

    let code = match yutani_codegen::protocol(spec) {
        Ok(code) => code,
        Err(error) => panic!("Failed to read protocol specification '{spec}': {error:?}")
    };
    let mut proto_file = match File::create(proto) {
        Ok(proto_file) => proto_file,
        Err(error) => panic!("Failed to create Rust source file '{proto}': {error:?}")
    };
    if let Err(error) = writeln!(proto_file, "// Auto-Generated file. Do not edit.\n#![allow(dead_code)]\n\n{}", code) {
        panic!("Failed to write Rust source file '{proto}': {error:?}")
    }
    if let Err(error) = Command::new("rustfmt").arg(proto).status() {
        panic!("Failed to run rustfmt on Rust source file '{proto}': {error:?}")
    }
}

Dependencies

~0.5–1.1MB
~25K SLoC