1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#![forbid(unsafe_code)]

use std::fs::File;
use std::io::Write;

use structopt::StructOpt;

use strum::VariantNames;
use strum_macros::{Display, EnumString, EnumVariantNames};

mod assemblyscript;
mod astype;
mod doc;
mod error;
mod overview;
mod pretty_writer;
mod rust;
mod zig;

pub use crate::error::*;

/// Generator output types
#[derive(Debug, Copy, Clone, PartialEq, Display, EnumString, EnumVariantNames)]
#[strum(serialize_all = "snake_case")]
pub enum OutputType {
    #[strum(serialize = "assemblyscript")]
    AssemblyScript,
    Rust,
    Zig,
    Overview,
    #[strum(serialize = "doc", serialize = "markdown")]
    Doc,
}

#[derive(Debug, Clone, PartialEq, StructOpt)]
pub struct Config {
    /// Set the module name to use instead of reading it from the witx file
    #[structopt(short, long)]
    pub module_name: Option<String>,

    /// Output file, or - for the standard output
    #[structopt(short, long)]
    pub output_file: Option<String>,

    /// WITX files
    #[structopt()]
    pub witx_files: Vec<String>,

    /// Output type
    #[structopt(short="t", long, possible_values=OutputType::VARIANTS)]
    pub output_type: OutputType,

    #[structopt(flatten)]
    pub flags: Options,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            module_name: None,
            output_file: None,
            witx_files: vec![],
            output_type: OutputType::Doc,
            flags: Options {
                skip_header: false,
                skip_imports: false,
            },
        }
    }
}

/// Options for WITX generators
#[derive(Debug, Clone, PartialEq, StructOpt)]
pub struct Options {
    /// Ignores imported types and functions
    #[structopt(short = "I", long)]
    skip_imports: bool,

    /// Do not generate a header
    #[structopt(short = "H", long)]
    skip_header: bool,
}

/// Abstract generator interface
pub trait Generator<T: Write> {
    fn generate(
        &self,
        writer: &mut T,
        module_witx: witx::Module,
        options: &Options,
    ) -> Result<(), Error>;
}

fn get_generator<T: Write>(module: Option<&str>, output: OutputType) -> Box<dyn Generator<T>> {
    let m = module.map(|v| v.to_string());

    match output {
        OutputType::AssemblyScript => Box::new(assemblyscript::AssemblyScriptGenerator::new(m)),
        OutputType::Zig => Box::new(zig::ZigGenerator::new(m)),
        OutputType::Rust => Box::new(rust::RustGenerator::new(m)),
        OutputType::Overview => Box::new(overview::OverviewGenerator::new(m)),
        OutputType::Doc => Box::new(doc::DocGenerator::new(m)),
    }
}

/// Generate sources from WITX files using the provided config
pub fn generate(cfg: &Config) -> Result<(), Error> {
    // generate all or generate no header no imports

    // Setup writer based on output file config
    let mut writer: Box<dyn Write> = match cfg.output_file.as_deref() {
        None | Some("-") => Box::new(std::io::stdout()),
        Some(file) => Box::new(File::create(file).unwrap()),
    };

    let mut flags = cfg.flags.clone();

    for witx_file in &cfg.witx_files {
        // Parse WITX file
        let witx = witx::load(witx_file).unwrap();

        // Create generator for the specified output type
        let generator = get_generator(cfg.module_name.as_deref(), cfg.output_type);

        // Generate output file
        generator.generate(&mut writer, witx, &flags).unwrap();

        // Generate definitions only once if we have multiple input files
        flags.skip_imports = true;
        flags.skip_header = true;
    }

    Ok(())
}