1#! /usr/bin/env python3 2# 3# Copyright © 2024 Collabora Ltd. and Red Hat Inc. 4# SPDX-License-Identifier: MIT 5 6# This script takes a list of Rust files, each of the form nvh_path_to_mod.rs 7# and constructs a lib.rs which puts each of them in ::path::to::mod. 8 9import argparse 10import os.path 11import re 12import subprocess 13import sys 14 15from mako.template import Template 16 17TEMPLATE_RS = Template("""\ 18// Copyright © 2024 Collabora Ltd. and Red Hat Inc. 19// SPDX-License-Identifier: MIT 20 21// This file is generated by lib_rs_gen.py. DO NOT EDIT! 22 23#![allow(unused_imports)] 24 25<%def name="import_mod(m, path)"> 26% if m.present: 27mod nvh_${'_'.join(path)}; 28% endif 29% for name in sorted(m.children): 30${import_mod(m.children[name], path + [name])} 31% endfor 32</%def> 33${import_mod(root, [])} 34 35<%def name="decl_mod(m, path)"> 36% if path: 37pub mod ${path[-1]} { 38% endif 39 40% if m.present: 41pub use crate::nvh_${'_'.join(path)}::*; 42% endif 43 44% for name in sorted(m.children): 45${decl_mod(m.children[name], path + [name])} 46% endfor 47 48% if path: 49} 50% endif 51</%def> 52 53/// Converts a method to its raw representation. 54pub trait Mthd { 55 /// The hardware address of the method. 56 const ADDR: u16; 57 /// The class of the method. 58 const CLASS: u16; 59 /// Converts the method to its raw representation. 60 fn to_bits(self) -> u32; 61} 62 63pub trait ArrayMthd { 64 /// The class of the method. 65 const CLASS: u16; 66 /// The hardware address of the method for the given index. 67 fn addr(i: usize) -> u16; 68 /// Converts the method to its raw representation. 69 fn to_bits(self) -> u32; 70} 71 72${decl_mod(root, [])} 73""") 74 75class Mod(object): 76 def __init__(self): 77 self.present = False; 78 self.children = {} 79 80 def add_child(self, path): 81 mod = self 82 for p in path: 83 if p not in mod.children: 84 mod.children[p] = Mod() 85 mod = mod.children[p] 86 87 # Once we've found the child, mark it present 88 assert not mod.present 89 mod.present = True 90 91def main(): 92 parser = argparse.ArgumentParser() 93 parser.add_argument('--out-rs', required=True, help='Output Rust file.') 94 parser.add_argument('class_files', metavar='FILE', nargs='*', 95 action='append', 96 help='Input class Rust filename') 97 args = parser.parse_args() 98 99 root = Mod() 100 for f in args.class_files[0]: 101 f = os.path.basename(f) 102 assert f.endswith('.rs') 103 f = f.removesuffix('.rs') 104 105 mod_path = f.split('_') 106 assert mod_path[0] == 'nvh' 107 root.add_child(mod_path[1:]) 108 109 try: 110 with open(args.out_rs, 'w', encoding='utf-8') as f: 111 f.write(TEMPLATE_RS.render(root=root)) 112 try: 113 subprocess.run(['rustfmt', args.out_rs], check=True) 114 except (subprocess.CalledProcessError, FileNotFoundError): 115 pass 116 117 except Exception: 118 # In the event there's an error, this imports some helpers from mako 119 # to print a useful stack trace and prints it, then exits with 120 # status 1, if python is run with debug; otherwise it just raises 121 # the exception 122 import sys 123 from mako import exceptions 124 print(exceptions.text_error_template().render(), file=sys.stderr) 125 sys.exit(1) 126 127if __name__ == '__main__': 128 main() 129