pyo3_stub_gen_derive/gen_stub.rs
1//! Code generation for embedding metadata for generating Python stub file.
2//!
3//! These metadata are embedded as `inventory::submit!` block like:
4//!
5//! ```rust
6//! # use pyo3::*;
7//! # use pyo3_stub_gen::type_info::*;
8//! # struct PyPlaceholder;
9//! inventory::submit!{
10//! PyClassInfo {
11//! pyclass_name: "Placeholder",
12//! module: Some("my_module"),
13//! struct_id: std::any::TypeId::of::<PyPlaceholder>,
14//! getters: &[
15//! MemberInfo {
16//! name: "name",
17//! r#type: <String as ::pyo3_stub_gen::PyStubType>::type_output,
18//! doc: "",
19//! default: None,
20//! },
21//! MemberInfo {
22//! name: "ndim",
23//! r#type: <usize as ::pyo3_stub_gen::PyStubType>::type_output,
24//! doc: "",
25//! default: None,
26//! },
27//! MemberInfo {
28//! name: "description",
29//! r#type: <Option<String> as ::pyo3_stub_gen::PyStubType>::type_output,
30//! doc: "",
31//! default: None,
32//! },
33//! ],
34//! setters: &[],
35//! doc: "",
36//! bases: &[],
37//! }
38//! }
39//! ```
40//!
41//! and this submodule responsible for generating such codes from Rust code like
42//!
43//! ```rust
44//! # use pyo3::*;
45//! #[pyclass(mapping, module = "my_module", name = "Placeholder")]
46//! #[derive(Debug, Clone)]
47//! pub struct PyPlaceholder {
48//! #[pyo3(get)]
49//! pub name: String,
50//! #[pyo3(get)]
51//! pub ndim: usize,
52//! #[pyo3(get)]
53//! pub description: Option<String>,
54//! pub custom_latex: Option<String>,
55//! }
56//! ```
57//!
58//! Mechanism
59//! ----------
60//! Code generation will take three steps:
61//!
62//! 1. Parse input [proc_macro2::TokenStream] into corresponding syntax tree component in [syn],
63//! - e.g. [ItemStruct] for `#[pyclass]`, [ItemImpl] for `#[pymethods]`, and so on.
64//! 2. Convert syntax tree components into `*Info` struct using [TryInto].
65//! - e.g. [PyClassInfo] is converted from [ItemStruct], [PyMethodsInfo] is converted from [ItemImpl], and so on.
66//! 3. Generate token streams using implementation of [quote::ToTokens] trait for `*Info` structs.
67//! - [quote::quote!] macro uses this trait.
68//!
69
70mod arg;
71mod attr;
72mod member;
73mod method;
74mod pyclass;
75mod pyclass_enum;
76mod pyfunction;
77mod pymethods;
78mod renaming;
79mod signature;
80mod stub_type;
81mod util;
82
83use arg::*;
84use attr::*;
85use member::*;
86use method::*;
87use pyclass::*;
88use pyclass_enum::*;
89use pyfunction::*;
90use pymethods::*;
91use renaming::*;
92use signature::*;
93use stub_type::*;
94use util::*;
95
96use proc_macro2::TokenStream as TokenStream2;
97use quote::quote;
98use syn::{parse2, ItemEnum, ItemFn, ItemImpl, ItemStruct, Result};
99
100pub fn pyclass(item: TokenStream2) -> Result<TokenStream2> {
101 let mut item_struct = parse2::<ItemStruct>(item)?;
102 let inner = PyClassInfo::try_from(item_struct.clone())?;
103 let derive_stub_type = StubType::from(&inner);
104 pyclass::prune_attrs(&mut item_struct);
105 Ok(quote! {
106 #item_struct
107 #derive_stub_type
108 pyo3_stub_gen::inventory::submit! {
109 #inner
110 }
111 })
112}
113
114pub fn pyclass_enum(item: TokenStream2) -> Result<TokenStream2> {
115 let inner = PyEnumInfo::try_from(parse2::<ItemEnum>(item.clone())?)?;
116 let derive_stub_type = StubType::from(&inner);
117 Ok(quote! {
118 #item
119 #derive_stub_type
120 pyo3_stub_gen::inventory::submit! {
121 #inner
122 }
123 })
124}
125
126pub fn pymethods(item: TokenStream2) -> Result<TokenStream2> {
127 let mut item_impl = parse2::<ItemImpl>(item)?;
128 let inner = PyMethodsInfo::try_from(item_impl.clone())?;
129 pymethods::prune_attrs(&mut item_impl);
130 Ok(quote! {
131 #item_impl
132 #[automatically_derived]
133 pyo3_stub_gen::inventory::submit! {
134 #inner
135 }
136 })
137}
138
139pub fn pyfunction(attr: TokenStream2, item: TokenStream2) -> Result<TokenStream2> {
140 let mut item_fn = parse2::<ItemFn>(item)?;
141 let mut inner = PyFunctionInfo::try_from(item_fn.clone())?;
142 inner.parse_attr(attr)?;
143 pyfunction::prune_attrs(&mut item_fn);
144 Ok(quote! {
145 #item_fn
146 #[automatically_derived]
147 pyo3_stub_gen::inventory::submit! {
148 #inner
149 }
150 })
151}