Skip to main content

pyo3_nest/
lib.rs

1// pyo3-nest
2use pyo3::prelude::*;
3
4/// adds multiple pyclasses to module
5#[macro_export]
6macro_rules! add_classes {
7    ($module:expr, $($class:ty),+ $(,)?) => {
8        $(
9            $module.add_class::<$class>()?;
10        )+
11    };
12}
13
14/// adds multiple pyfunctions to module
15#[macro_export]
16macro_rules! add_functions {
17    ($module:expr, $($func:ident),+ $(,)?) => {
18        $(
19            $module.add_function(wrap_pyfunction!($func, $module)?)?;
20        )+
21    };
22}
23
24/// Creates nested submodules with beautiful, clean syntax.
25///
26/// # Examples
27///
28/// ```rust
29/// submodule!(m, "routing", add_classes!(Router, Route));
30///
31/// submodule!(m, "responses",
32///     add_classes!(JSONResponse, HTMLResponse) &&
33///     add_functions!(create_response)
34/// );
35///
36/// submodule!(m, "responses.types", add_classes!(TextType, ImageType, AlienType));
37/// ```
38#[macro_export]
39macro_rules! submodule {
40    (@resolve $parent:expr, $path:literal) => {{
41    let py = $parent.py();
42    let parts: Vec<&str> = $path.split('.')
43        .filter(|s| !s.is_empty())
44        .collect();
45    let mut current: Bound<'_, PyModule> = $parent.clone();
46    let sys_modules = py.import("sys")?.getattr("modules")?;
47
48    let root_name: String = {
49        let raw: String = current.name()?.to_string();
50        let segments: Vec<&str> = raw.split('.').collect();
51        if segments.len() >= 2 && segments[segments.len()-1] == segments[segments.len()-2] {
52            segments[..segments.len()-1].join(".")
53        } else {
54            raw
55        }
56    };
57
58    for &part in &parts {
59        let full_name = format!("{}.{}", root_name, part);
60        current = if let Ok(existing) = current.getattr(part) {
61            existing.cast_into::<PyModule>()?
62        } else {
63            let new_mod = PyModule::new(py, part)?;
64            new_mod.setattr("__name__", &full_name)?;
65            new_mod.setattr("__package__", &root_name)?;
66            current.add_submodule(&new_mod)?;
67            sys_modules.set_item(&full_name, &new_mod)?;
68            new_mod
69        };}
70        current
71    }};
72
73    // only classes
74    ($parent:expr, $path:literal, add_classes!($($class:ty),+ $(,)?)) => {{
75        let leaf = submodule!(@resolve $parent, $path);
76        add_classes!(leaf, $($class),+);
77    }};
78
79    // only functions
80    ($parent:expr, $path:literal, add_functions!($($func:ident),+ $(,)?)) => {{
81        let leaf = submodule!(@resolve $parent, $path);
82        add_functions!(leaf, $($func),+);
83    }};
84
85    // classes && functions
86    ($parent:expr, $path:literal,
87     add_classes!($($class:ty),+ $(,)?) && add_functions!($($func:ident),+ $(,)?)) => {{
88        let leaf = submodule!(@resolve $parent, $path);
89        add_classes!(leaf, $($class),+);
90        add_functions!(leaf, $($func),+);
91    }};
92
93    // functions && classes
94    ($parent:expr, $path:literal,
95     add_functions!($($func:ident),+ $(,)?) && add_classes!($($class:ty),+ $(,)?)) => {{
96        let leaf = submodule!(@resolve $parent, $path);
97        add_functions!(leaf, $($func),+);
98        add_classes!(leaf, $($class),+);
99    }};
100}