_core/
lib.rs

1use pyo3::prelude::*;
2use pyo3::types::PyDict;
3use std::path::PathBuf;
4
5use crate::config::ProjectConfig;
6
7mod config;
8mod engine;
9
10// Macro to extract config values from PyDict with default values
11macro_rules! extract_config {
12    // Pattern for required values (no default)
13    ($dict:expr, $key:literal, required) => {{
14        if let Some(value) = $dict.get_item($key)? {
15            value.extract()?
16        } else {
17            return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(concat!(
18                $key,
19                " is required"
20            )));
21        }
22    }};
23
24    // Pattern for optional values with default (handles all types)
25    ($dict:expr, $key:literal, $default:expr) => {{
26        if let Some(value) = $dict.get_item($key)? {
27            value.extract()?
28        } else {
29            $default
30        }
31    }};
32}
33
34#[pyfunction]
35fn get_crate_version() -> String {
36    env!("CARGO_PKG_VERSION").to_string()
37}
38
39/// The new engine-based function to generate a project
40#[pyfunction]
41fn run_engine(config_dict: &Bound<'_, PyDict>) -> PyResult<bool> {
42    // Convert the Python dictionary to a Rust ProjectConfig struct
43    let project_name: String = extract_config!(config_dict, "project_name", required);
44    let destination: String = extract_config!(config_dict, "destination", ".".to_string());
45    let destination_path = PathBuf::from(destination);
46
47    // Extract other configuration options with defaults or Optionals
48    let author_name: String = extract_config!(config_dict, "author_name", "Test User".to_string());
49    let author_email: String =
50        extract_config!(config_dict, "author_email", "test@example.com".to_string());
51
52    let db_name: Option<String> = extract_config!(
53        config_dict,
54        "db_name",
55        Some(project_name.to_lowercase().replace('-', "_"))
56    );
57
58    let db_owner_admin: Option<String> = extract_config!(
59        config_dict,
60        "db_owner_admin",
61        Some(format!(
62            "{}_owner",
63            project_name.to_lowercase().replace('-', "_")
64        ))
65    );
66
67    let db_owner_pword: Option<String> =
68        extract_config!(config_dict, "db_owner_pword", Some("password".to_string()));
69
70    let include_server: bool = extract_config!(config_dict, "include_server", true);
71    let include_frontend: bool = extract_config!(config_dict, "include_frontend", true);
72    let include_tauri_desktop: bool = extract_config!(config_dict, "include_tauri_desktop", true);
73
74    let app_identifier: String = extract_config!(
75        config_dict,
76        "app_identifier",
77        format!(
78            "com.example.{}",
79            project_name.to_lowercase().replace('-', "")
80        )
81    );
82
83    let deno_package_name: String = extract_config!(
84        config_dict,
85        "deno_package_name",
86        "@test/gwa-project".to_string()
87    );
88
89    // Create the ProjectConfig struct
90    let project_config = ProjectConfig {
91        project_name: project_name.clone(),
92        author_name,
93        author_email,
94        app_identifier,
95        db_name,
96        db_owner_admin,
97        db_owner_pword,
98        include_server,
99        include_frontend,
100        include_tauri_desktop,
101        deno_package_name,
102    };
103
104    // Generate the project using the new engine
105    match engine::run(&project_config, &destination_path) {
106        Ok(_) => Ok(true),
107        Err(e) => Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(format!(
108            "Failed to generate project with engine: {}",
109            e
110        ))),
111    }
112}
113
114/// A Python module implemented in Rust. The name of this function must match
115/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
116/// import the module.
117#[pymodule]
118fn _core(m: &Bound<PyModule>) -> PyResult<()> {
119    // m.add_function(wrap_pyfunction!(hello_from_bin, m)?)?;
120    // m.add_function(wrap_pyfunction!(init, m)?)?;
121    m.add_function(wrap_pyfunction!(get_crate_version, m)?)?;
122    m.add_function(wrap_pyfunction!(run_engine, m)?)?;
123    Ok(())
124}