use crate::{Error as EngineError, IncludeCppEngine, RebuildDependencyRecorder};
use proc_macro2::TokenStream;
use quote::ToTokens;
use std::{fmt::Display, io::Read, path::PathBuf};
use std::{panic::UnwindSafe, path::Path, rc::Rc};
use syn::Item;
#[derive(Debug)]
pub enum ParseError {
FileOpen(std::io::Error),
FileRead(std::io::Error),
Syntax(syn::Error),
AutocxxCodegenError(EngineError),
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ParseError::FileOpen(err) => write!(f, "Unable to open file: {}", err)?,
ParseError::FileRead(err) => write!(f, "Unable to read file: {}", err)?,
ParseError::Syntax(err) => write!(f, "Syntax error parsing Rust file: {}", err)?,
ParseError::AutocxxCodegenError(err) => {
write!(f, "Unable to parse include_cpp! macro: {}", err)?
}
}
Ok(())
}
}
pub fn parse_file<P1: AsRef<Path>>(rs_file: P1) -> Result<ParsedFile, ParseError> {
let mut source = String::new();
let mut file = std::fs::File::open(rs_file).map_err(ParseError::FileOpen)?;
file.read_to_string(&mut source)
.map_err(ParseError::FileRead)?;
proc_macro2::fallback::force();
let source = syn::parse_file(&source).map_err(ParseError::Syntax)?;
parse_file_contents(source)
}
fn parse_file_contents(source: syn::File) -> Result<ParsedFile, ParseError> {
let mut results = Vec::new();
for item in source.items {
if let Item::Macro(ref mac) = item {
if mac.mac.path.is_ident("include_cpp") {
let include_cpp = crate::IncludeCppEngine::new_from_syn(mac.mac.clone())
.map_err(ParseError::AutocxxCodegenError)?;
results.push(Segment::Autocxx(include_cpp));
continue;
}
}
results.push(Segment::Other(item));
}
Ok(ParsedFile(results))
}
pub struct ParsedFile(Vec<Segment>);
#[allow(clippy::large_enum_variant)]
enum Segment {
Autocxx(IncludeCppEngine),
Other(Item),
}
impl ParsedFile {
pub fn get_autocxxes(&self) -> Vec<&IncludeCppEngine> {
self.0
.iter()
.filter_map(|s| match s {
Segment::Autocxx(includecpp) => Some(includecpp),
Segment::Other(_) => None,
})
.collect()
}
pub fn get_autocxxes_mut(&mut self) -> Vec<&mut IncludeCppEngine> {
self.0
.iter_mut()
.filter_map(|s| match s {
Segment::Autocxx(includecpp) => Some(includecpp),
Segment::Other(_) => None,
})
.collect()
}
pub fn resolve_all(
&mut self,
autocxx_inc: Vec<PathBuf>,
dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
) -> Result<(), ParseError> {
let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> =
dep_recorder.map(Rc::from);
for include_cpp in self.get_autocxxes_mut() {
let dep_recorder: Option<Box<dyn RebuildDependencyRecorder>> = match &inner_dep_recorder
{
None => None,
Some(inner_dep_recorder) => Some(Box::new(CompositeDepRecorder::new(
inner_dep_recorder.clone(),
))),
};
include_cpp
.generate(autocxx_inc.clone(), dep_recorder)
.map_err(ParseError::AutocxxCodegenError)?
}
Ok(())
}
}
impl ToTokens for ParsedFile {
fn to_tokens(&self, tokens: &mut TokenStream) {
for seg in &self.0 {
match seg {
Segment::Other(item) => item.to_tokens(tokens),
Segment::Autocxx(autocxx) => {
let these_tokens = autocxx.generate_rs();
tokens.extend(these_tokens);
}
}
}
}
}
#[derive(Debug, Clone)]
struct CompositeDepRecorder(Rc<dyn RebuildDependencyRecorder>);
impl CompositeDepRecorder {
fn new(inner: Rc<dyn RebuildDependencyRecorder>) -> Self {
CompositeDepRecorder(inner)
}
}
impl UnwindSafe for CompositeDepRecorder {}
impl RebuildDependencyRecorder for CompositeDepRecorder {
fn record_header_file_dependency(&self, filename: &str) {
self.0.record_header_file_dependency(filename);
}
}