use std::collections::BTreeSet;
use std::fmt::Write as _;
use std::path::Path;
use syn::visit::Visit;
pub struct Bridge {
pub file: &'static str,
pub func: &'static str,
pub recv: &'static str,
}
pub const BRIDGES: &[Bridge] = &[
b("shared.rs", "str_core", "Str"),
b("shared.rs", "color_core", "Str"),
b("shared.rs", "num_core", "*"),
b("shared.rs", "char_method", "Char"),
b("shared.rs", "regex_core", "Regex"),
b("shared.rs", "match_core", "Match"),
b("shared.rs", "captures_core", "Captures"),
b("shared.rs", "duration_core", "Duration"),
b("shared.rs", "datetime_core", "DateTime"),
b("shared.rs", "status_core", "Status"),
b("shared.rs", "header_value_core", "HeaderValue"),
b("shared.rs", "exit_status_core", "ExitStatus"),
b("shared.rs", "json_type_test", "*"),
b("int_methods.rs", "int_method", "*"),
b("int_methods.rs", "int_arith_method", "*"),
b("int_methods.rs", "int_query_method", "*"),
b("int_methods.rs", "big_int_method", "*"),
b("methods.rs", "json_value_method", "*"),
b("methods.rs", "str_method", "Str"),
b("methods.rs", "str_method_slow", "Str"),
b("methods.rs", "opt_method", "Option"),
b("methods.rs", "res_method", "Result"),
b("methods.rs", "entry_method", "Entry"),
b("methods.rs", "ordering_method", "Ordering"),
b("cell.rs", "cell_method", "Cell"),
b("native_methods.rs", "io_error_method", "Native"),
b("native_methods.rs", "joinerr_method", "Native"),
b("methods.rs", "generic_method", "*"),
b("vecmap.rs", "vec_method", "Vec"),
b("vecmap.rs", "vec_get", "Vec"),
b("vecmap.rs", "edge_element_ref", "Vec"),
b("vecmap.rs", "vec_method_by_name", "Vec"),
b("vecmap.rs", "vec_copy_from_slice", "Vec"),
b("vecmap.rs", "vec_min_max", "Vec"),
b("vecmap.rs", "map_method", "Map"),
b("higher_order.rs", "higher_order", "*"),
b("higher_order.rs", "vec_higher_order", "Vec"),
b("higher_order.rs", "vec_transform_ho", "Vec"),
b("higher_order.rs", "vec_reduce_ho", "Vec"),
b("higher_order.rs", "vec_order_ho", "Vec"),
b("higher_order.rs", "option_higher_order", "Option"),
b("higher_order.rs", "result_higher_order", "Result"),
b("higher_order.rs", "entry_higher_order", "Entry"),
b("iterator.rs", "iterator_method", "Iterator"),
b("iterator.rs", "iterator_higher_order", "Iterator"),
b("iterator.rs", "iterator_predicate", "Iterator"),
b("bridge.rs", "eval_method", "*"),
b("bridge.rs", "pre_dispatch", "*"),
b("bridge.rs", "deref_receiver", "*"),
b("bridge.rs", "method_by_receiver", "*"),
b("bridge.rs", "scalar_method", "*"),
b("bridge.rs", "range_builtin", "*"),
b("bridge.rs", "native_method", "Native"),
b("bridge.rs", "exitstatus_method", "ExitStatus"),
b("bridge.rs", "output_method", "Output"),
b("bridge.rs", "duration_method", "Duration"),
b("bridge.rs", "datetime_method", "DateTime"),
b("std_bridge.rs", "path_method", "Path"),
b("std_bridge.rs", "metadata_method", "Metadata"),
b("std_bridge.rs", "os_string_method", "OsString"),
b("std_bridge.rs", "dir_entry_method", "DirEntry"),
b("std_bridge.rs", "file_type_method", "FileType"),
b("std_bridge.rs", "std_stream_method", "Native"),
b("std_bridge.rs", "openoptions_method", "OpenOptions"),
b("native_methods.rs", "reader_native_method", "Native"),
b("native_methods.rs", "writer_native_method", "Native"),
b("native_methods.rs", "file_native_method", "Native"),
b("native_methods.rs", "child_native_method", "Native"),
b("native_methods.rs", "net_native_method", "Native"),
b("native_methods.rs", "udp_native_method", "Native"),
b("native_methods.rs", "time_native_method", "Native"),
b("native_methods.rs", "temp_native_method", "Native"),
b("process.rs", "command_method", "Command"),
b("process.rs", "child_method", "Child"),
b("regex_bridge.rs", "regex_method", "Regex"),
b("http.rs", "request_method", "Request"),
b("http.rs", "client_method", "Client"),
b("http.rs", "builder_method", "Builder"),
b("http.rs", "response_method", "Response"),
b("http.rs", "header_map_method", "HeaderMap"),
b("crates_bridge.rs", "base64_method", "Base64"),
b("crates_bridge.rs", "rng_method", "Rng"),
b("crates_bridge.rs", "sha256_method", "Sha256"),
b("pdf_bridge.rs", "document_method", "Document"),
b("xmltree_bridge.rs", "element_method", "Element"),
b("ratatui_render.rs", "style_method", "Style"),
b("ratatui_render.rs", "modifier_method", "Modifier"),
b("ratatui_render.rs", "span_method", "Span"),
b("ratatui_render.rs", "line_method", "Line"),
b("ratatui_render.rs", "cell_method", "Cell"),
b("ratatui_render.rs", "row_method", "Row"),
b("ratatui_render.rs", "table_method", "Table"),
b("ratatui_render.rs", "block_method", "Block"),
b("ratatui_render.rs", "sparkline_method", "Sparkline"),
b("ratatui_render.rs", "buffer_method", "Buffer"),
b("ratatui_render.rs", "buffer_cell_method", "BufferCell"),
b("winreg_bridge.rs", "regkey_method", "RegKey"),
b("service_bridge.rs", "service_method", "Service"),
b("service_bridge.rs", "manager_method", "ServiceManager"),
b("wmi_bridge.rs", "wmi_method", "WmiConnection"),
];
const fn b(file: &'static str, func: &'static str, recv: &'static str) -> Bridge {
Bridge { file, func, recv }
}
#[derive(Default)]
struct LitCollector {
names: BTreeSet<String>,
}
impl LitCollector {
fn take(&mut self, value: String) {
if !value.is_empty()
&& value
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
{
self.names.insert(value);
}
}
fn take_tokens(&mut self, tokens: proc_macro2::TokenStream) {
for tree in tokens {
match tree {
proc_macro2::TokenTree::Literal(lit) => {
let text = lit.to_string();
if let Some(inner) = text.strip_prefix('"').and_then(|t| t.strip_suffix('"')) {
self.take(inner.to_string());
}
}
proc_macro2::TokenTree::Group(group) => self.take_tokens(group.stream()),
_ => {}
}
}
}
}
impl<'ast> Visit<'ast> for LitCollector {
fn visit_macro(&mut self, mac: &'ast syn::Macro) {
self.take_tokens(mac.tokens.clone());
syn::visit::visit_macro(self, mac);
}
fn visit_lit_str(&mut self, lit: &'ast syn::LitStr) {
self.take(lit.value());
}
}
struct FnFinder<'a> {
want: &'a str,
found: Option<BTreeSet<String>>,
}
impl<'ast> Visit<'ast> for FnFinder<'_> {
fn visit_item_fn(&mut self, item: &'ast syn::ItemFn) {
if item.sig.ident == self.want {
let mut c = LitCollector::default();
c.visit_block(&item.block);
self.found.get_or_insert_with(BTreeSet::new).extend(c.names);
}
syn::visit::visit_item_fn(self, item);
}
fn visit_impl_item_fn(&mut self, item: &'ast syn::ImplItemFn) {
if item.sig.ident == self.want {
let mut c = LitCollector::default();
c.visit_block(&item.block);
self.found.get_or_insert_with(BTreeSet::new).extend(c.names);
}
syn::visit::visit_impl_item_fn(self, item);
}
}
fn harvest(dir: &Path, file: &str, func: &str) -> Option<BTreeSet<String>> {
let text = std::fs::read_to_string(dir.join(file)).ok()?;
let ast = syn::parse_file(&text).ok()?;
let mut finder = FnFinder {
want: func,
found: None,
};
finder.visit_file(&ast);
finder.found
}
fn harvest_names(dir: &Path, file: &str, func: &str) -> BTreeSet<String> {
harvest(dir, file, func).unwrap_or_default()
}
pub fn generate(interpreter_dir: &Path) -> String {
let mut out = String::new();
out.push_str(
"// Generated by build.rs from the bridge sources. Do not edit.\n\
// See src/bridge_tables_build.rs for how and why.\n\n",
);
let mut rows: Vec<String> = Vec::new();
for bridge in BRIDGES {
let names = harvest(interpreter_dir, bridge.file, bridge.func).unwrap_or_else(|| {
panic!(
"bridge function `{}` not found in {}. It was renamed or moved, \
which would silently empty its coverage table.",
bridge.func, bridge.file
)
});
let list: Vec<String> = names.iter().map(|n| format!("{n:?}")).collect();
rows.push(format!(
" BridgeTable {{ recv: {:?}, names: &[{}] }},",
bridge.recv,
list.join(", ")
));
}
let _ = writeln!(
out,
"pub const BRIDGE_TABLES: &[BridgeTable] = &[\n{}\n];\n",
rows.join("\n")
);
let builtin = harvest_names(interpreter_dir, "bytecode.rs", "resolve");
let list: Vec<String> = builtin.iter().map(|n| format!("{n:?}")).collect();
let _ = writeln!(
out,
"pub const BUILTIN_IDS: &[&str] = &[{}];\n",
list.join(", ")
);
out
}