ligen_python_parser/module/
mod.rs1use crate::prelude::*;
2use ligen::{ir::Module, parser::ParserConfig};
3use rustpython_parser::ast::ModModule;
4use crate::parser::PythonParser;
5
6#[derive(Default)]
7pub struct ModuleParser;
8
9impl Parser<&str> for ModuleParser {
10 type Output = WithSource<ModModule>;
11 fn parse(&self, input: &str, _config: &ParserConfig) -> Result<Self::Output> {
12 let module = parse(input, Mode::Module, "<embedded>")
13 .map_err(|error| Error::Message(format!("Failed to parse module: {}", error)))?
14 .module()
15 .ok_or(Error::Message("No module found".into()))?;
16 Ok(WithSource::new(input, module))
17 }
18}
19
20impl Parser<WithSource<ModModule>> for PythonParser {
21 type Output = Module;
22 fn parse(&self, input: WithSource<ModModule>, config: &ParserConfig) -> Result<Self::Output> {
23 let scope = self.parse(input.sub(input.ast.body.as_slice()), config)?;
24 let imports = scope.imports;
25 let objects = scope.objects;
26 let types = scope.types;
27 let functions = scope.functions;
28 let interfaces = scope.interfaces;
29 Ok(Module { objects, functions, types, interfaces, imports, .. Default::default() })
30 }
31}
32
33pub(crate) struct Directory<'a>(pub &'a std::path::Path);
34pub(crate) struct File<'a>(pub &'a std::path::Path);
35pub(crate) struct SubPath<'a>(pub &'a std::path::Path);
36
37impl Parser<File<'_>> for PythonParser {
38 type Output = Module;
39 fn parse(&self, File(input): File<'_>, config: &ParserConfig) -> Result<Self::Output> {
40 let content = std::fs::read_to_string(input)?;
41 let module = ModuleParser.parse(content.as_str(), config)?;
42 let mut module = self.parse(module, config)?;
43 module.identifier = self.identifier_parser.parse(input, config)?;
44 Ok(module)
45 }
46}
47
48impl Parser<Directory<'_>> for PythonParser {
49 type Output = Module;
50 fn parse(&self, Directory(input): Directory<'_>, config: &ParserConfig) -> Result<Self::Output> {
51 let identifier = self.identifier_parser.parse(input, config)?;
52 let mut module = Module { identifier, .. Default::default() };
53 let mut modules: Vec<Module> = Vec::new();
54 for entry in input.read_dir()? {
55 let entry = entry?;
56 let path = entry.path();
57 let extension = path
58 .extension()
59 .and_then(|extension| extension.to_str())
60 .map(String::from)
61 .unwrap_or_default();
62 if extension == "py" || extension == "pyi" || path.is_dir() {
63 if let Ok(module) = self.parse(SubPath(path.as_path()), config) {
64 if let Some(existing) = modules
65 .iter_mut()
66 .find(|existing| existing.identifier == module.identifier)
67 {
68 existing.join(module)
69 } else {
70 modules.push(module);
71 }
72 }
73 }
74 }
75 if let Some((index, _)) = modules
76 .iter()
77 .enumerate()
78 .find(|(_, sub_module)| sub_module.identifier.name == "__init__")
79 {
80 let identifier = module.identifier;
81 module = modules.remove(index);
82 module.identifier = identifier;
83 }
84 module.modules = modules;
85 Ok(module)
86 }
87}
88
89impl Parser<SubPath<'_>> for PythonParser {
90 type Output = Module;
91 fn parse(&self, SubPath(input): SubPath<'_>, config: &ParserConfig) -> Result<Self::Output> {
92 let input = if input.with_extension("py").exists() {
93 input.with_extension("py")
94 } else {
95 input.to_path_buf()
96 };
97
98 let input = input.as_path();
99
100 if input.is_dir() {
101 self.parse(Directory(input), config)
102 } else {
103 self.parse(File(input), config)
104 .map_err(|error| Error::Message(format!("Failed to read {}. Cause: {:?}", input.display(), error)))
105 }
106 }
107}