Skip to main content

pipi/
settings.rs

1//! Defines configurable application settings.
2
3use std::env;
4
5use heck::ToSnakeCase;
6#[allow(unused_imports)]
7use rhai::{CustomType, Dynamic, EvalAltResult, Position, TypeBuilder};
8use serde::{Deserialize, Serialize};
9
10use crate::{
11    wizard::{self, AssetsOption, BackgroundOption, DBOption},
12    PIPI_VERSION, OS,
13};
14
15/// Represents general application settings.
16#[derive(Serialize, Deserialize, Clone, Debug, CustomType)]
17pub struct Settings {
18    pub package_name: String,
19    pub module_name: String,
20    pub db: Option<Db>,
21    pub background: Option<Background>,
22    pub asset: Option<Asset>,
23    pub auth: bool,
24    pub mailer: bool,
25    pub initializers: Option<Initializers>,
26    pub features: Features,
27    pub pipi_version_text: String,
28    pub os: OS,
29}
30
31impl From<DBOption> for Option<Db> {
32    fn from(db_option: DBOption) -> Self {
33        match db_option {
34            DBOption::None => None,
35            _ => Some(Db {
36                kind: db_option.clone(),
37                endpoint: db_option.endpoint_config().to_string(),
38            }),
39        }
40    }
41}
42
43impl From<BackgroundOption> for Option<Background> {
44    fn from(bg: BackgroundOption) -> Self {
45        Some(Background { kind: bg })
46    }
47}
48
49impl From<AssetsOption> for Option<Asset> {
50    fn from(asset: AssetsOption) -> Self {
51        match asset {
52            AssetsOption::None => None,
53            _ => Some(Asset { kind: asset }),
54        }
55    }
56}
57
58impl Settings {
59    /// Creates a new [`Settings`] instance based on prompt selections.
60    #[must_use]
61    pub fn from_wizard(package_name: &str, prompt_selection: &wizard::Selections, os: OS) -> Self {
62        let features = if prompt_selection.db.enable() {
63            Features::default()
64        } else {
65            let mut features = Features::disable_features();
66            if matches!(prompt_selection.background, wizard::BackgroundOption::Queue) {
67                features.names.push("bg_redis".to_string());
68            }
69            features
70        };
71
72        Self {
73            package_name: package_name.to_string(),
74            module_name: package_name.to_snake_case(),
75            auth: prompt_selection.db.enable(),
76            mailer: prompt_selection.db.enable(),
77            db: prompt_selection.db.clone().into(),
78            background: prompt_selection.background.clone().into(),
79            asset: prompt_selection.asset.clone().into(),
80            initializers: if prompt_selection.asset == AssetsOption::Serverside {
81                Some(Initializers { view_engine: true })
82            } else {
83                None
84            },
85            features,
86            pipi_version_text: get_pipi_version_text(),
87            os,
88        }
89    }
90}
91impl Default for Settings {
92    fn default() -> Self {
93        #[allow(clippy::default_trait_access)]
94        Self {
95            package_name: Default::default(),
96            module_name: Default::default(),
97            db: Default::default(),
98            background: Default::default(),
99            asset: Default::default(),
100            auth: Default::default(),
101            mailer: Default::default(),
102            initializers: Default::default(),
103            features: Default::default(),
104            pipi_version_text: get_pipi_version_text(),
105            os: Default::default(),
106        }
107    }
108}
109
110fn get_pipi_version_text() -> String {
111    env::var("PIPI_DEV_MODE_PATH").map_or_else(
112        |_| format!(r#"version = "{PIPI_VERSION}""#),
113        |path| {
114            let path = path.replace('\\', "/");
115            format!(r#"version = "{PIPI_VERSION}", path = "{path}""#)
116        },
117    )
118}
119
120/// Database configuration settings.
121#[derive(Serialize, Deserialize, Clone, Debug, Default, CustomType)]
122pub struct Db {
123    pub kind: DBOption,
124    pub endpoint: String,
125}
126
127/// Background processing configuration.
128#[derive(Serialize, Deserialize, Clone, Debug, Default, CustomType)]
129pub struct Background {
130    pub kind: BackgroundOption,
131}
132
133/// Asset configuration settings.
134#[derive(Serialize, Deserialize, Clone, Debug, Default, CustomType)]
135pub struct Asset {
136    pub kind: AssetsOption,
137}
138
139#[derive(Serialize, Deserialize, Clone, Debug, Default, CustomType)]
140pub struct Initializers {
141    pub view_engine: bool,
142}
143
144/// Feature configuration, allowing toggling of optional features.
145#[derive(Serialize, Deserialize, Clone, Debug)]
146pub struct Features {
147    pub default_features: bool,
148    pub names: Vec<String>,
149}
150
151impl Default for Features {
152    fn default() -> Self {
153        Self {
154            default_features: true,
155            names: vec![],
156        }
157    }
158}
159
160impl Features {
161    /// Disables default features.
162    #[must_use]
163    pub fn disable_features() -> Self {
164        Self {
165            default_features: false,
166            names: vec!["cli".to_string()],
167        }
168    }
169}