1mod block;
5mod builtin;
6mod cfg;
7mod check;
8pub(super) mod error;
9mod file;
10pub(super) mod fs;
11mod ifndef;
12pub(super) mod include;
13mod names;
14mod namespace;
15mod nested;
16pub(super) mod out;
17mod write;
18
19use self::cfg::UnsupportedCfgEvaluator;
20use self::error::{format_err, Result};
21use self::file::File;
22use self::include::Include;
23use crate::syntax::cfg::CfgExpr;
24use crate::syntax::report::Errors;
25use crate::syntax::{self, attrs, Types};
26use std::collections::BTreeSet as Set;
27use std::path::Path;
28
29pub(super) use self::error::Error;
30
31#[non_exhaustive]
46pub struct Opt {
47 pub include: Vec<Include>,
51 pub cxx_impl_annotations: Option<String>,
57 pub cfg_evaluator: Box<dyn CfgEvaluator>,
59
60 pub(super) gen_header: bool,
61 pub(super) gen_implementation: bool,
62 pub(super) allow_dot_includes: bool,
63 pub(super) doxygen: bool,
64}
65
66pub trait CfgEvaluator {
69 fn eval(&self, name: &str, value: Option<&str>) -> CfgResult;
72}
73
74pub enum CfgResult {
76 True,
78 False,
80 Undetermined {
82 msg: String,
84 },
85}
86
87#[derive(Default)]
89pub struct GeneratedCode {
90 pub header: Vec<u8>,
92 pub implementation: Vec<u8>,
94}
95
96impl Default for Opt {
97 fn default() -> Self {
98 Opt {
99 include: Vec::new(),
100 cxx_impl_annotations: None,
101 gen_header: true,
102 gen_implementation: true,
103 allow_dot_includes: true,
104 cfg_evaluator: Box::new(UnsupportedCfgEvaluator),
105 doxygen: false,
106 }
107 }
108}
109
110pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
111 let source = match read_to_string(path) {
112 Ok(source) => source,
113 Err(err) => format_err(path, "", err),
114 };
115 match generate_from_string(&source, opt) {
116 Ok(out) => out,
117 Err(err) => format_err(path, &source, err),
118 }
119}
120
121fn read_to_string(path: &Path) -> Result<String> {
122 let bytes = if path == Path::new("-") {
123 fs::read_stdin()
124 } else {
125 fs::read(path)
126 }?;
127 match String::from_utf8(bytes) {
128 Ok(string) => Ok(string),
129 Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
130 }
131}
132
133fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
134 let mut source = source;
135 if source.starts_with("#!") && !source.starts_with("#![") {
136 let shebang_end = source.find('\n').unwrap_or(source.len());
137 source = &source[shebang_end..];
138 }
139 let syntax: File = syn::parse_str(source)?;
140 generate(syntax, opt)
141}
142
143pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
144 if syntax.modules.is_empty() {
145 return Err(Error::NoBridgeMod);
146 }
147
148 let ref mut apis = Vec::new();
149 let ref mut errors = Errors::new();
150 let ref mut cfg_errors = Set::new();
151 for bridge in syntax.modules {
152 let mut cfg = CfgExpr::Unconditional;
153 attrs::parse(
154 errors,
155 bridge.attrs,
156 attrs::Parser {
157 cfg: Some(&mut cfg),
158 ignore_unrecognized: true,
159 ..Default::default()
160 },
161 );
162 if cfg::eval(errors, cfg_errors, opt.cfg_evaluator.as_ref(), &cfg) {
163 let ref namespace = bridge.namespace;
164 let trusted = bridge.unsafety.is_some();
165 apis.extend(syntax::parse_items(
166 errors,
167 bridge.content,
168 trusted,
169 namespace,
170 ));
171 }
172 }
173
174 cfg::strip(errors, cfg_errors, opt.cfg_evaluator.as_ref(), apis);
175 errors.propagate()?;
176
177 let ref types = Types::collect(errors, apis);
178 check::precheck(errors, apis, opt);
179 errors.propagate()?;
180
181 let generator = check::Generator::Build;
182 check::typecheck(errors, apis, types, generator);
183 errors.propagate()?;
184
185 let (mut header, mut implementation) = Default::default();
189 if opt.gen_header {
190 header = write::gen(apis, types, opt, true);
191 }
192 if opt.gen_implementation {
193 implementation = write::gen(apis, types, opt, false);
194 }
195 Ok(GeneratedCode {
196 header,
197 implementation,
198 })
199}