witx_codegen/
lib.rs

1#![forbid(unsafe_code)]
2
3use std::fs::File;
4use std::io::Write;
5
6use structopt::StructOpt;
7use strum::VariantNames;
8use strum_macros::{Display, EnumString, VariantNames};
9
10mod assemblyscript;
11mod astype;
12mod cpp;
13mod doc;
14mod error;
15mod overview;
16mod pretty_writer;
17mod rust;
18mod weet;
19mod zig;
20
21pub use crate::error::*;
22
23/// Generator output types
24#[derive(Debug, Copy, Clone, PartialEq, Eq, Display, EnumString, VariantNames)]
25#[strum(serialize_all = "snake_case")]
26pub enum OutputType {
27    #[strum(serialize = "assemblyscript")]
28    AssemblyScript,
29    Rust,
30    Zig,
31    Overview,
32    #[strum(serialize = "doc", serialize = "markdown")]
33    Doc,
34    Cpp,
35    Weet,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, StructOpt)]
39pub struct Config {
40    /// Set the module name to use instead of reading it from the witx file
41    #[structopt(short, long)]
42    pub module_name: Option<String>,
43
44    /// Output file, or - for the standard output
45    #[structopt(short, long)]
46    pub output_file: Option<String>,
47
48    /// WITX files
49    #[structopt()]
50    pub witx_files: Vec<String>,
51
52    /// Output type
53    #[structopt(short="t", long, possible_values=OutputType::VARIANTS)]
54    pub output_type: OutputType,
55
56    #[structopt(flatten)]
57    pub flags: Options,
58}
59
60impl Default for Config {
61    fn default() -> Self {
62        Self {
63            module_name: None,
64            output_file: None,
65            witx_files: vec![],
66            output_type: OutputType::Doc,
67            flags: Options {
68                skip_header: false,
69                skip_imports: false,
70            },
71        }
72    }
73}
74
75/// Options for WITX generators
76#[derive(Debug, Clone, PartialEq, Eq, StructOpt)]
77pub struct Options {
78    /// Ignores imported types and functions
79    #[structopt(short = "I", long)]
80    skip_imports: bool,
81
82    /// Do not generate a header
83    #[structopt(short = "H", long)]
84    skip_header: bool,
85}
86
87/// Abstract generator interface
88pub trait Generator<T: Write> {
89    fn generate(
90        &self,
91        writer: &mut T,
92        module_witx: witx::Module,
93        options: &Options,
94    ) -> Result<(), Error>;
95}
96
97fn get_generator<T: Write>(module: Option<&str>, output: OutputType) -> Box<dyn Generator<T>> {
98    let m = module.map(|v| v.to_string());
99
100    match output {
101        OutputType::AssemblyScript => Box::new(assemblyscript::AssemblyScriptGenerator::new(m)),
102        OutputType::Zig => Box::new(zig::ZigGenerator::new(m)),
103        OutputType::Rust => Box::new(rust::RustGenerator::new(m)),
104        OutputType::Overview => Box::new(overview::OverviewGenerator::new(m)),
105        OutputType::Doc => Box::new(doc::DocGenerator::new(m)),
106        OutputType::Cpp => Box::new(cpp::CppGenerator::new(m)),
107        OutputType::Weet => Box::new(weet::WeetGenerator::new(m)),
108    }
109}
110
111/// Generate sources from WITX files using the provided config
112pub fn generate(cfg: &Config) -> Result<(), Error> {
113    // generate all or generate no header no imports
114
115    // Setup writer based on output file config
116    let mut writer: Box<dyn Write> = match cfg.output_file.as_deref() {
117        None | Some("-") => Box::new(std::io::stdout()),
118        Some(file) => Box::new(File::create(file).unwrap()),
119    };
120
121    let mut flags = cfg.flags.clone();
122
123    for witx_file in &cfg.witx_files {
124        // Parse WITX file
125        let witx = witx::load(witx_file).unwrap();
126
127        // Create generator for the specified output type
128        let generator = get_generator(cfg.module_name.as_deref(), cfg.output_type);
129
130        // Generate output file
131        generator.generate(&mut writer, witx, &flags).unwrap();
132
133        // Generate definitions only once if we have multiple input files
134        flags.skip_imports = true;
135        flags.skip_header = true;
136    }
137
138    Ok(())
139}