pub(crate) mod attribute_parser;
pub(crate) mod function_extractor;
pub(crate) mod function_parser;
pub(crate) mod internal_config;
pub(crate) mod misc;
pub(crate) mod reader;
pub(crate) mod source_graph;
pub(crate) mod type_alias_resolver;
pub(crate) mod type_parser;
use crate::codegen::dumper::Dumper;
use crate::codegen::ir::pack::IrPack;
use crate::codegen::misc::GeneratorProgressBarPack;
use crate::codegen::parser::function_extractor::extract_generalized_functions_from_file;
use crate::codegen::parser::function_parser::FunctionParser;
use crate::codegen::parser::internal_config::ParserInternalConfig;
use crate::codegen::parser::misc::parse_has_executor;
use crate::codegen::parser::reader::CachedRustReader;
use crate::codegen::parser::type_alias_resolver::resolve_type_aliases;
use crate::codegen::parser::type_parser::TypeParser;
use crate::codegen::ConfigDumpContent;
use itertools::Itertools;
use log::trace;
use std::path::{Path, PathBuf};
use syn::File;
use ConfigDumpContent::SourceGraph;
pub(crate) fn parse(
config: &ParserInternalConfig,
cached_rust_reader: &mut CachedRustReader,
dumper: &Dumper,
progress_bar_pack: &GeneratorProgressBarPack,
) -> anyhow::Result<IrPack> {
let rust_input_paths = &config.rust_input_path_pack.rust_input_paths;
trace!("rust_input_paths={:?}", &rust_input_paths);
let file_data_arr = read_files(
rust_input_paths,
&config.rust_crate_dir,
cached_rust_reader,
dumper,
progress_bar_pack,
)?;
let pb = progress_bar_pack.parse_source_graph.start();
let crate_map = source_graph::crates::Crate::parse(
&config.rust_crate_dir.join("Cargo.toml"),
cached_rust_reader,
dumper,
)?;
dumper.dump(SourceGraph, "source_graph.json", &crate_map)?;
drop(pb);
let src_fns = file_data_arr
.iter()
.map(|file| extract_generalized_functions_from_file(&file.ast, &file.path))
.collect::<anyhow::Result<Vec<_>>>()?
.into_iter()
.flatten()
.collect_vec();
let src_structs = crate_map.root_module().collect_structs();
let src_enums = crate_map.root_module().collect_enums();
let src_types = resolve_type_aliases(crate_map.root_module().collect_types());
let mut type_parser = TypeParser::new(src_structs, src_enums, src_types);
let mut function_parser = FunctionParser::new(&mut type_parser);
let ir_funcs = src_fns
.iter()
.map(|f| {
function_parser.parse_function(&f.generalized_item_fn, &f.path, &config.rust_crate_dir)
})
.collect::<anyhow::Result<Vec<_>>>()?
.into_iter()
.flatten()
.sorted_by_cached_key(|func| func.name.clone())
.collect_vec();
let has_executor = (file_data_arr.iter()).any(|file| parse_has_executor(&file.content));
let (struct_pool, enum_pool) = type_parser.consume();
Ok(IrPack {
funcs: ir_funcs,
struct_pool,
enum_pool,
has_executor,
})
}
struct FileData {
path: PathBuf,
content: String,
ast: File,
}
fn read_files(
rust_input_paths: &[PathBuf],
rust_crate_dir: &Path,
cached_rust_reader: &mut CachedRustReader,
dumper: &Dumper,
progress_bar_pack: &GeneratorProgressBarPack,
) -> anyhow::Result<Vec<FileData>> {
let _pb = progress_bar_pack.parse_cargo_expand.start();
let contents = rust_input_paths
.iter()
.map(|rust_input_path| {
let content =
cached_rust_reader.read_rust_file(rust_input_path, rust_crate_dir, dumper)?;
Ok((rust_input_path.to_owned(), content))
})
.collect::<anyhow::Result<Vec<(PathBuf, String)>>>()?;
contents
.into_iter()
.map(|(rust_input_path, content)| {
let ast = syn::parse_file(&content)?;
Ok(FileData {
path: rust_input_path,
content,
ast,
})
})
.collect()
}
#[cfg(test)]
mod tests {
use crate::codegen::config::internal_config::RustInputPathPack;
use crate::codegen::dumper::Dumper;
use crate::codegen::misc::GeneratorProgressBarPack;
use crate::codegen::parser::internal_config::ParserInternalConfig;
use crate::codegen::parser::parse;
use crate::codegen::parser::reader::CachedRustReader;
use crate::codegen::parser::source_graph::crates::Crate;
use crate::utils::logs::configure_opinionated_test_logging;
use crate::utils::test_utils::{
create_path_sanitizers, get_test_fixture_dir, json_golden_test,
};
use log::info;
use serial_test::serial;
use std::path::Path;
#[test]
#[serial]
fn test_simple() -> anyhow::Result<()> {
body("library/codegen/parser/mod/simple", None)
}
#[test]
#[serial]
fn test_methods() -> anyhow::Result<()> {
body("library/codegen/parser/mod/methods", None)
}
#[test]
#[serial]
fn test_multi_input_file() -> anyhow::Result<()> {
body(
"library/codegen/parser/mod/multi_input_file",
Some(Box::new(|rust_crate_dir| RustInputPathPack {
rust_input_paths: [
rust_crate_dir.join("src/api_one.rs"),
rust_crate_dir.join("src/api_two.rs"),
]
.into(),
})),
)
}
#[test]
#[serial]
fn test_use_type_in_another_file() -> anyhow::Result<()> {
body("library/codegen/parser/mod/use_type_in_another_file", None)
}
#[test]
#[serial]
fn test_qualified_names() -> anyhow::Result<()> {
body("library/codegen/parser/mod/qualified_names", None)
}
#[test]
#[serial]
fn test_non_qualified_names() -> anyhow::Result<()> {
body("library/codegen/parser/mod/non_qualified_names", None)
}
#[test]
#[serial]
fn test_generics() -> anyhow::Result<()> {
body("library/codegen/parser/mod/generics", None)
}
#[allow(clippy::type_complexity)]
fn body(
fixture_name: &str,
rust_input_path_pack: Option<Box<dyn Fn(&Path) -> RustInputPathPack>>,
) -> anyhow::Result<()> {
configure_opinionated_test_logging();
let test_fixture_dir = get_test_fixture_dir(fixture_name);
let rust_crate_dir = test_fixture_dir.clone();
info!("test_fixture_dir={test_fixture_dir:?}");
let crate_map = Crate::parse(
&rust_crate_dir.join("Cargo.toml"),
&mut CachedRustReader::default(),
&Dumper(&Default::default()),
)?;
json_golden_test(
&serde_json::to_value(crate_map)?,
&rust_crate_dir.join("expect_source_graph.json"),
&create_path_sanitizers(&test_fixture_dir),
)?;
let actual_ir = parse(
&ParserInternalConfig {
rust_input_path_pack: rust_input_path_pack.map(|f| f(&rust_crate_dir)).unwrap_or(
RustInputPathPack {
rust_input_paths: vec![rust_crate_dir.join("src/api.rs")],
},
),
rust_crate_dir: rust_crate_dir.clone(),
},
&mut CachedRustReader::default(),
&Dumper(&Default::default()),
&GeneratorProgressBarPack::new(),
)?;
json_golden_test(
&serde_json::to_value(actual_ir)?,
&rust_crate_dir.join("expect_ir.json"),
&[],
)?;
Ok(())
}
}