use crate::ir::{IRBody, IRConstant, IRExpr, IRFunction, IRModule, IRStatement, IRType};
use anyhow::Result;
use std::path::Path;
#[derive(Debug)]
pub struct EntryPointInfo {
pub main_function_name: String,
pub detected_type: EntryPointType,
pub contains_args_parsing: bool,
}
#[derive(Debug, PartialEq)]
pub enum EntryPointType {
MainPyFile, MainNameCheck, CliScript, NoEntryPointDetected,
}
pub fn detect_entry_points(
source: &str,
file_path: Option<&Path>,
) -> Result<Option<EntryPointInfo>> {
if let Some(path) = file_path {
if path
.file_name()
.is_some_and(|name| name.to_string_lossy() == "__main__.py")
{
return Ok(Some(EntryPointInfo {
main_function_name: "main".to_string(),
detected_type: EntryPointType::MainPyFile,
contains_args_parsing: source.contains("sys.argv") || source.contains("argparse"),
}));
}
}
if source.contains("if __name__ == \"__main__\"")
|| source.contains("if __name__ == '__main__'")
{
let main_function_name = if source.contains("def main(") {
"main".to_string()
} else {
"main".to_string()
};
let contains_args_parsing = source.contains("argparse")
|| source.contains("sys.argv")
|| source.contains("import click")
|| source.contains("import typer");
return Ok(Some(EntryPointInfo {
main_function_name,
detected_type: EntryPointType::MainNameCheck,
contains_args_parsing,
}));
}
let is_cli_module = source.contains("argparse.ArgumentParser")
|| source.contains("import click")
|| source.contains("import typer")
|| (source.contains("import sys") && source.contains("sys.argv"));
if is_cli_module {
let main_function_name = if source.contains("def main(") {
"main".to_string()
} else {
"main".to_string()
};
return Ok(Some(EntryPointInfo {
main_function_name,
detected_type: EntryPointType::CliScript,
contains_args_parsing: true,
}));
}
Ok(None)
}
pub fn create_main_function_from_entry_point(
_source: &str,
entry_point_info: &EntryPointInfo,
) -> Result<IRFunction> {
let body_statements = match entry_point_info.detected_type {
EntryPointType::MainNameCheck | EntryPointType::CliScript => {
vec![
IRStatement::Return(Some(IRExpr::FunctionCall {
function_name: entry_point_info.main_function_name.clone(),
arguments: Vec::new(),
})),
]
}
EntryPointType::MainPyFile => {
vec![
IRStatement::Expression(IRExpr::FunctionCall {
function_name: entry_point_info.main_function_name.clone(),
arguments: Vec::new(),
}),
IRStatement::Return(Some(IRExpr::Const(IRConstant::Int(0)))),
]
}
_ => {
vec![IRStatement::Return(Some(IRExpr::Const(IRConstant::Int(0))))]
}
};
let main_function = IRFunction {
name: "main".to_string(), params: Vec::new(), return_type: IRType::Int, decorators: Vec::new(),
body: IRBody {
statements: body_statements,
},
};
Ok(main_function)
}
pub fn add_entry_point_to_module(
module: &mut IRModule,
entry_point_info: &EntryPointInfo,
) -> Result<()> {
let main_function = create_main_function_from_entry_point("", entry_point_info)?;
let main_exists = module.functions.iter().any(|f| f.name == "main");
if !main_exists {
module.functions.push(main_function);
}
module
.metadata
.insert("has_entry_point".to_string(), "true".to_string());
module.metadata.insert(
"entry_point_type".to_string(),
format!("{:?}", entry_point_info.detected_type),
);
Ok(())
}