vst3_bindgen/
lib.rs

1//! A binding generator for the VST 3 API. `vst3-bindgen` can be used to generate Rust bindings for
2//! the VST 3 API from the original C++ headers.
3
4use std::collections::HashSet;
5use std::error::Error;
6use std::io::Write;
7use std::path::{Path, PathBuf};
8use std::{fs, io};
9
10fn find_headers<P: AsRef<Path>>(dir: P) -> Result<Vec<PathBuf>, io::Error> {
11    fn find_headers_inner<P: AsRef<Path>>(dir: P, headers: &mut Vec<PathBuf>) -> io::Result<()> {
12        for entry in fs::read_dir(dir)? {
13            let entry = entry?;
14            let metadata = entry.metadata()?;
15
16            let path = entry.path();
17            if metadata.file_type().is_dir() {
18                find_headers_inner(path, headers)?;
19            } else {
20                if path.extension().map(|ext| ext == "h").unwrap_or(false) {
21                    headers.push(path);
22                }
23            }
24        }
25
26        Ok(())
27    }
28
29    let mut headers = Vec::new();
30    find_headers_inner(dir, &mut headers)?;
31
32    Ok(headers)
33}
34
35fn parse_iid(tokens: &[String]) -> Option<String> {
36    if let Some(first) = tokens.first() {
37        if first == "DECLARE_CLASS_IID" {
38            return Some(format!(
39                "pub const {}_iid: TUID = uid({}, {}, {}, {});",
40                tokens[2], tokens[4], tokens[6], tokens[8], tokens[10]
41            ));
42        }
43    }
44
45    None
46}
47
48/// Generates Rust bindings given a path to the VST 3 SDK.
49pub fn generate(
50    sdk_dir: &Path,
51    target: Option<&str>,
52    mut sink: impl Write,
53) -> Result<(), Box<dyn Error>> {
54    let pluginterfaces_path = sdk_dir.join("pluginterfaces");
55    let headers = find_headers(&pluginterfaces_path)?;
56
57    let skip_headers = HashSet::from([
58        Path::new("pluginterfaces/base/funknownimpl.h"),
59        Path::new("pluginterfaces/base/ustring.h"),
60        Path::new("pluginterfaces/test/itest.h"),
61        Path::new("pluginterfaces/vst/ivsttestplugprovider.h"),
62    ]);
63
64    let mut source = String::new();
65    for header in &headers {
66        let relative = header.strip_prefix(sdk_dir).unwrap();
67        if skip_headers.contains(relative) {
68            continue;
69        }
70
71        let name = relative.to_str().unwrap();
72
73        use std::fmt::Write;
74        writeln!(source, "#include \"{}\"", name)?;
75    }
76
77    writeln!(sink, "{}", include_str!("support.rs"))?;
78
79    let mut generator = com_scrape::Generator::default()
80        .skip_types(&[
81            "Adopt",
82            "ConstStringTable",
83            "FUID",
84            "FReleaser",
85            "LARGE_INT",
86        ])
87        .skip_interface_trait("FUnknown")
88        .constant_parser(parse_iid)
89        .iid_generator(|name| format!("tuid_as_guid({name}_iid)"))
90        .query_interface_fn("FUnknown_query_interface")
91        .add_ref_fn("FUnknown_add_ref")
92        .release_fn("FUnknown_release")
93        .include_path(sdk_dir);
94
95    if let Some(target) = target {
96        generator = generator.target(target);
97    }
98
99    generator.generate(source, &mut sink)?;
100
101    Ok(())
102}