#![feature(proc_macro_span)]
use std::path::Path;
use std::fs;
use proc_macro::{ Span, TokenStream };
use proc_macro_error::{
proc_macro_error,
abort,
};
use python_ast::{
CodeGen, CodeGenContext,
PythonOptions, PyResult,
parse,
tree::Module,
SymbolTableScopes
};
use quote::{quote, format_ident};
fn load_module(mod_name: String) -> PyResult<Module> {
let mod_path = Span::mixed_site().parent()
.expect(format!("unable to determine parent of module for {}", mod_name).as_str())
.source_file().path();
let mod_name_dir = format!("src/{}/__init__.py", &mod_name);
let mod_name_file = format!("src/{}.py", &mod_name);
let python_str = if Path::new(&mod_name_dir).exists() {
fs::read_to_string(mod_name_dir)
} else if Path::new(&mod_name_file).exists() {
fs::read_to_string(mod_name_file)
} else {
abort!(mod_name, "Module not found {} in {:?}", mod_name, mod_path)
}.expect("Reading python module");
parse(&python_str, &mod_name)
}
fn module_options(input: TokenStream, options: PythonOptions) -> TokenStream {
let symbols = SymbolTableScopes::new();
let mut new_input = input.into_iter();
let mod_name = new_input.next()
.expect("missing module name").to_string();
let remaining_input = proc_macro::TokenStream::from_iter(new_input);
let mut remaining_input2 = proc_macro2::TokenStream::from(remaining_input);
let py_mod = load_module(mod_name.clone())
.expect("loading python module ");
let py_output = py_mod.to_rust(CodeGenContext::Module, options, symbols)
.expect("converting Python to Rust");
proc_macro2::TokenStream::extend::<proc_macro2::TokenStream>(&mut remaining_input2, py_output.into());
let new_mod_name = format_ident!("{}", mod_name);
let result = quote!(mod #new_mod_name {
#remaining_input2
});
result.into()
}
#[proc_macro]
#[proc_macro_error]
pub fn python_module(input: TokenStream) -> TokenStream {
let options = PythonOptions::default();
module_options(input, options)
}
#[proc_macro]
#[proc_macro_error]
pub fn python_module_nostd(input: TokenStream) -> TokenStream {
let mut options = PythonOptions::default();
options.with_std_python = false;
module_options(input, options)
}