1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! Store of metadata for generating Python stub file
//!
//! Stub file generation takes two steps:
//!
//! Store metadata (compile time)
//! ------------------------------
//! Embed compile-time information about Rust types and PyO3 macro arguments
//! using [inventory::submit!](https://docs.rs/inventory/latest/inventory/macro.submit.html) macro into source codes,
//! and these information will be gathered by [inventory::iter](https://docs.rs/inventory/latest/inventory/struct.iter.html).
//! This submodule is responsible for this process.
//!
//! - [PyClassInfo] stores information obtained from `#[pyclass]` macro
//! - [PyMethodsInfo] stores information obtained from `#[pymethods]` macro
//!
//! and others are their components.
//!
//! Gathering metadata and generating stub file (runtime)
//! -------------------------------------------------------
//! Since `#[pyclass]` and `#[pymethods]` definitions are not bundled in a single block,
//! we have to reconstruct these information corresponding to a Python `class`.
//! This process is done at runtime in [gen_stub](../../gen_stub) executable.
//!

use crate::{PyStubType, TypeInfo};
use std::any::TypeId;

/// Work around for `CompareOp` for `__richcmp__` argument,
/// which does not implements `FromPyObject`
pub fn compare_op_type_input() -> TypeInfo {
    <isize as PyStubType>::type_input()
}

pub fn no_return_type_output() -> TypeInfo {
    TypeInfo::none()
}

/// Info of method argument appears in `#[pymethods]`
#[derive(Debug)]
pub struct ArgInfo {
    pub name: &'static str,
    pub r#type: fn() -> TypeInfo,
}

/// Info of usual method appears in `#[pymethod]`
#[derive(Debug)]
pub struct MethodInfo {
    pub name: &'static str,
    pub args: &'static [ArgInfo],
    pub r#return: fn() -> TypeInfo,
    pub signature: Option<&'static str>,
    pub doc: &'static str,
    pub is_static: bool,
    pub is_class: bool,
}

/// Info of getter method decorated with `#[getter]` or `#[pyo3(get, set)]` appears in `#[pyclass]`
#[derive(Debug)]
pub struct MemberInfo {
    pub name: &'static str,
    pub r#type: fn() -> TypeInfo,
}

/// Info of `#[new]`-attributed methods appears in `#[pymethods]`
#[derive(Debug)]
pub struct NewInfo {
    pub args: &'static [ArgInfo],
    pub signature: Option<&'static str>,
}

/// Info of `#[pymethod]`
#[derive(Debug)]
pub struct PyMethodsInfo {
    // The Rust struct type-id of `impl` block where `#[pymethod]` acts on
    pub struct_id: fn() -> TypeId,
    /// Method specified `#[new]` attribute
    pub new: Option<NewInfo>,
    /// Methods decorated with `#[getter]`
    pub getters: &'static [MemberInfo],
    /// Other usual methods
    pub methods: &'static [MethodInfo],
}

inventory::collect!(PyMethodsInfo);

/// Info of `#[pyclass]` with Rust struct
#[derive(Debug)]
pub struct PyClassInfo {
    // Rust struct type-id
    pub struct_id: fn() -> TypeId,
    // The name exposed to Python
    pub pyclass_name: &'static str,
    /// Module name specified by `#[pyclass(module = "foo.bar")]`
    pub module: Option<&'static str>,
    /// Docstring
    pub doc: &'static str,
    /// static members by `#[pyo3(get, set)]`
    pub members: &'static [MemberInfo],
}

inventory::collect!(PyClassInfo);

/// Info of `#[pyclass]` with Rust enum
#[derive(Debug)]
pub struct PyEnumInfo {
    // Rust struct type-id
    pub enum_id: fn() -> TypeId,
    // The name exposed to Python
    pub pyclass_name: &'static str,
    /// Module name specified by `#[pyclass(module = "foo.bar")]`
    pub module: Option<&'static str>,
    /// Docstring
    pub doc: &'static str,
    /// Variants of enum
    pub variants: &'static [&'static str],
}

inventory::collect!(PyEnumInfo);

/// Info of `#[pyfunction]`
#[derive(Debug)]
pub struct PyFunctionInfo {
    pub name: &'static str,
    pub args: &'static [ArgInfo],
    pub r#return: fn() -> TypeInfo,
    pub doc: &'static str,
    pub signature: Option<&'static str>,
    pub module: Option<&'static str>,
}

inventory::collect!(PyFunctionInfo);

#[derive(Debug)]
pub struct PyErrorInfo {
    pub name: &'static str,
    pub module: &'static str,
    pub base: fn() -> &'static str,
}

inventory::collect!(PyErrorInfo);

#[derive(Debug)]
pub struct PyVariableInfo {
    pub name: &'static str,
    pub module: &'static str,
    pub r#type: fn() -> TypeInfo,
}

inventory::collect!(PyVariableInfo);