Skip to content

Commit fff4d8c

Browse files
authored
✨ Initial addition of functions (#16)
1 parent 1caaaca commit fff4d8c

File tree

19 files changed

+303
-66
lines changed

19 files changed

+303
-66
lines changed

crates/analyzer/src/analyze.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
pub mod crate_;
44
pub mod enum_;
5+
pub mod function;
56
pub mod module;
67
pub mod struct_;
78
pub mod type_;

crates/analyzer/src/analyze/crate_.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use anyhow::{Context, Result};
33
use serde::{Deserialize, Serialize};
44

5-
use crate::data_model::{Crate, Enum, Module, Struct};
5+
use crate::data_model::{Crate, Enum, Function, Module, Struct};
66

77
pub fn analyze_crate(path: &str) -> Result<AnalysisResult> {
88
// make the path absolute
@@ -56,15 +56,10 @@ pub fn analyze_crate(path: &str) -> Result<AnalysisResult> {
5656
)));
5757
};
5858

59-
let mut result = AnalysisResult {
60-
crate_: Crate {
61-
name: crate_name,
62-
version: cargo_toml.package.version.clone(),
63-
},
64-
modules: vec![],
65-
structs: vec![],
66-
enums: vec![],
67-
};
59+
let mut result = AnalysisResult::new(Crate {
60+
name: crate_name,
61+
version: cargo_toml.package.version.clone(),
62+
});
6863

6964
// check existence of the root module
7065
let root_module = path.join(to_root);
@@ -74,7 +69,7 @@ pub fn analyze_crate(path: &str) -> Result<AnalysisResult> {
7469

7570
// read the top-level module
7671
let content = std::fs::read_to_string(&root_module)?;
77-
let (module, structs, enums) =
72+
let (module, structs, enums, functions) =
7873
Module::parse(Some(&root_module), &[&result.crate_.name], &content).context(format!(
7974
"Error parsing module {}",
8075
root_module.to_string_lossy()
@@ -94,6 +89,7 @@ pub fn analyze_crate(path: &str) -> Result<AnalysisResult> {
9489
result.modules.push(module);
9590
result.structs.extend(structs);
9691
result.enums.extend(enums);
92+
result.functions.extend(functions);
9793

9894
// recursively find/read the public sub-modules
9995
let mut read_modules = vec![];
@@ -121,7 +117,7 @@ pub fn analyze_crate(path: &str) -> Result<AnalysisResult> {
121117

122118
let content = std::fs::read_to_string(&module_path)?;
123119
let path: Vec<String> = [&parent[..], &[module_name]].concat();
124-
let (module, structs, enums) = Module::parse(
120+
let (module, structs, enums, functions) = Module::parse(
125121
Some(&module_path),
126122
&path.iter().map(|s| s.as_str()).collect::<Vec<&str>>(),
127123
&content,
@@ -140,6 +136,7 @@ pub fn analyze_crate(path: &str) -> Result<AnalysisResult> {
140136
result.modules.push(module);
141137
result.structs.extend(structs);
142138
result.enums.extend(enums);
139+
result.functions.extend(functions);
143140
}
144141

145142
Ok(result)
@@ -152,6 +149,19 @@ pub struct AnalysisResult {
152149
pub modules: Vec<Module>,
153150
pub structs: Vec<Struct>,
154151
pub enums: Vec<Enum>,
152+
pub functions: Vec<Function>,
153+
}
154+
155+
impl AnalysisResult {
156+
pub fn new(crate_: Crate) -> Self {
157+
Self {
158+
crate_,
159+
modules: vec![],
160+
structs: vec![],
161+
enums: vec![],
162+
functions: vec![],
163+
}
164+
}
155165
}
156166

157167
#[derive(Debug, Deserialize)]
@@ -308,6 +318,7 @@ mod tests {
308318
- DummyEnum2
309319
docstring: The enum2 docstring
310320
variants: []
321+
functions: []
311322
"###);
312323

313324
Ok(())
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,42 @@
1+
use crate::data_model::Function;
12

3+
use super::docstring_from_attrs;
4+
5+
impl Function {
6+
/// Fully qualified name of the variant
7+
pub fn path_str(&self) -> String {
8+
self.path.join("::")
9+
}
10+
pub fn parse(parent: &[&str], ast: &syn::ItemFn) -> Self {
11+
let name = ast.sig.ident.to_string();
12+
let path: Vec<&str> = parent.iter().copied().chain(Some(name.as_str())).collect();
13+
let docstring = docstring_from_attrs(&ast.attrs);
14+
Self {
15+
path: path.iter().map(|s| s.to_string()).collect(),
16+
docstring,
17+
}
18+
}
19+
}
20+
21+
#[cfg(test)]
22+
mod tests {
23+
use super::*;
24+
25+
use insta::assert_yaml_snapshot;
26+
27+
#[test]
28+
fn test_function_parse() {
29+
let item: syn::ItemFn = syn::parse_quote! {
30+
/// This is a docstring
31+
pub fn my_function() {}
32+
};
33+
let func = Function::parse(&["my_module"], &item);
34+
assert_yaml_snapshot!(func, @r###"
35+
---
36+
path:
37+
- my_module
38+
- my_function
39+
docstring: This is a docstring
40+
"###);
41+
}
42+
}

crates/analyzer/src/analyze/macro.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.

crates/analyzer/src/analyze/module.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::path::Path;
44
use anyhow::Result;
55
use syn::parse_file;
66

7-
use crate::data_model::{Enum, Module, Struct};
7+
use crate::data_model::{Enum, Function, Module, Struct};
88

99
use super::docstring_from_attrs;
1010

@@ -18,7 +18,7 @@ impl Module {
1818
file: Option<&Path>,
1919
path: &[&str],
2020
content: &str,
21-
) -> Result<(Self, Vec<Struct>, Vec<Enum>)> {
21+
) -> Result<(Self, Vec<Struct>, Vec<Enum>, Vec<Function>)> {
2222
let syntax = parse_file(content)?;
2323
let mut mod_ = Self {
2424
file: file.map(|f| f.to_string_lossy().to_string()), // TODO better way to serialize the path, also ?
@@ -29,6 +29,7 @@ impl Module {
2929

3030
let mut structs = vec![];
3131
let mut enums = vec![];
32+
let mut functions = vec![];
3233

3334
for item in syntax.items {
3435
// TODO traits, functions, impls, et
@@ -51,11 +52,17 @@ impl Module {
5152
enums.push(enum_);
5253
}
5354
}
55+
syn::Item::Fn(fn_item) => {
56+
if let syn::Visibility::Public(_) = fn_item.vis {
57+
let function = Function::parse(path, fn_item);
58+
functions.push(function);
59+
}
60+
}
5461
_ => {}
5562
}
5663
}
5764

58-
Ok((mod_, structs, enums))
65+
Ok((mod_, structs, enums, functions))
5966
}
6067

6168
pub fn to_json(&self) -> String {
@@ -99,6 +106,7 @@ pub enum MyEnum {
99106
docstring: ""
100107
discriminant: ~
101108
fields: []
109+
- []
102110
"###);
103111
}
104112
}

crates/analyzer/src/analyze/trait.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.

crates/analyzer/src/analyze/variable.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.

crates/analyzer/src/data_model.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ pub struct Field {
8383
pub type_: TypeSignature,
8484
}
8585

86+
#[derive(Debug, Clone, Serialize, Deserialize)]
87+
/// Representation of a function
88+
pub struct Function {
89+
/// The fully qualified name of the function.
90+
pub path: Vec<String>,
91+
/// The docstring of the function
92+
pub docstring: String,
93+
// TODO signature
94+
}
95+
8696
#[derive(Debug, Clone, Serialize, Deserialize)]
8797
/// A segment of a type signature
8898
///

crates/py_binding/src/data_model.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,37 @@ impl From<data_model::TypeSegment> for TypeSegment {
260260
}
261261
}
262262
}
263+
264+
#[pyclass]
265+
#[derive(Clone)]
266+
/// pyo3 representation of a function
267+
pub struct Function {
268+
#[pyo3(get)]
269+
pub path: Vec<String>,
270+
#[pyo3(get)]
271+
pub docstring: String,
272+
}
273+
274+
#[pymethods]
275+
impl Function {
276+
pub fn __repr__(&self) -> String {
277+
format!("Function({:?})", self.path_str())
278+
}
279+
#[getter]
280+
pub fn path_str(&self) -> String {
281+
self.path.join("::")
282+
}
283+
#[getter]
284+
pub fn name(&self) -> String {
285+
self.path.last().unwrap().clone()
286+
}
287+
}
288+
289+
impl From<data_model::Function> for Function {
290+
fn from(field: data_model::Function) -> Self {
291+
Function {
292+
path: field.path,
293+
docstring: field.docstring,
294+
}
295+
}
296+
}

crates/py_binding/src/data_query.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
33
use pyo3::{exceptions::PyIOError, prelude::*};
44

5-
use analyzer::data_model as analyze_model;
5+
use analyzer::data_model::{self as analyze_model};
66

7-
use crate::data_model::{Crate, Enum, Module, Struct};
7+
use crate::data_model::{Crate, Enum, Function, Module, Struct};
88

99
fn read_file(path: &std::path::Path) -> PyResult<String> {
1010
match std::fs::read_to_string(path) {
@@ -90,6 +90,20 @@ pub fn load_enum(cache_path: &str, full_name: &str) -> PyResult<Option<Enum>> {
9090
Ok(Some(enum_.into()))
9191
}
9292

93+
#[pyfunction]
94+
/// load a function from the cache, if it exists
95+
pub fn load_function(cache_path: &str, full_name: &str) -> PyResult<Option<Function>> {
96+
let path = std::path::Path::new(cache_path)
97+
.join("functions")
98+
.join(format!("{}.json", full_name));
99+
if !path.exists() {
100+
return Ok(None);
101+
}
102+
let contents = read_file(&path)?;
103+
let func: analyze_model::Function = deserialize_object(full_name, &contents)?;
104+
Ok(Some(func.into()))
105+
}
106+
93107
/// Check if a path is a child of a given parent, and return the fully qualified name of the child.
94108
fn is_child(path: &std::path::PathBuf, parent: &Vec<String>) -> Option<String> {
95109
let name = match path.file_stem() {
@@ -178,6 +192,28 @@ pub fn load_child_enums(cache_path: &str, parent: Vec<String>) -> PyResult<Vec<E
178192
Ok(enums)
179193
}
180194

195+
#[pyfunction]
196+
/// load all function from the cache that are children of the given parent
197+
pub fn load_child_functions(cache_path: &str, parent: Vec<String>) -> PyResult<Vec<Function>> {
198+
let path = std::path::Path::new(cache_path).join("functions");
199+
if !path.exists() {
200+
return Ok(vec![]);
201+
}
202+
let mut funcs = vec![];
203+
for entry in std::fs::read_dir(path)? {
204+
let entry = entry?;
205+
let path = entry.path();
206+
if path.is_file() {
207+
if let Some(name) = is_child(&path, &parent) {
208+
let contents = read_file(&path)?;
209+
let func: analyze_model::Function = deserialize_object(&name, &contents)?;
210+
funcs.push(func.into());
211+
}
212+
}
213+
}
214+
Ok(funcs)
215+
}
216+
181217
/// Check if a path is an ancestor of a given parent, and return the fully qualified name of the child.
182218
fn is_ancestor(
183219
path: &std::path::PathBuf,

0 commit comments

Comments
 (0)