Skip to main content

goud_engine/sdk/
engine_config.rs

1//! High-level engine configuration builder.
2//!
3//! [`EngineConfig`] combines [`GameConfig`] (window/display settings) with
4//! [`ProviderRegistryBuilder`] (subsystem backends) into a single fluent
5//! builder. This is the recommended way to configure and launch the engine.
6//!
7//! # Example
8//!
9//! ```rust
10//! use goud_engine::sdk::EngineConfig;
11//!
12//! let config = EngineConfig::new()
13//!     .with_title("My Game")
14//!     .with_size(1280, 720)
15//!     .with_vsync(true);
16//!
17//! let (game_config, providers) = config.build();
18//! assert_eq!(game_config.title, "My Game");
19//! ```
20
21use crate::core::providers::audio::AudioProvider;
22use crate::core::providers::input::InputProvider;
23use crate::core::providers::physics::PhysicsProvider;
24use crate::core::providers::render::RenderProvider;
25use crate::core::providers::ProviderRegistry;
26use crate::core::providers::ProviderRegistryBuilder;
27use crate::sdk::game_config::GameConfig;
28
29/// Unified engine configuration combining window settings and provider selection.
30///
31/// `EngineConfig` delegates window/display settings to [`GameConfig`] and
32/// provider selection to [`ProviderRegistryBuilder`]. Call [`build()`](Self::build)
33/// to consume the builder and obtain both parts.
34pub struct EngineConfig {
35    game_config: GameConfig,
36    provider_builder: ProviderRegistryBuilder,
37}
38
39impl EngineConfig {
40    /// Creates a new `EngineConfig` with default settings and null providers.
41    pub fn new() -> Self {
42        Self {
43            game_config: GameConfig::default(),
44            provider_builder: ProviderRegistryBuilder::new(),
45        }
46    }
47
48    // Window / Display Settings (delegated to GameConfig)
49
50    /// Sets the window title.
51    pub fn with_title(mut self, title: impl Into<String>) -> Self {
52        self.game_config = self.game_config.with_title(title);
53        self
54    }
55
56    /// Sets the window dimensions.
57    pub fn with_size(mut self, width: u32, height: u32) -> Self {
58        self.game_config = self.game_config.with_size(width, height);
59        self
60    }
61
62    /// Enables or disables vertical sync.
63    pub fn with_vsync(mut self, enabled: bool) -> Self {
64        self.game_config = self.game_config.with_vsync(enabled);
65        self
66    }
67
68    /// Enables or disables fullscreen mode.
69    pub fn with_fullscreen(mut self, enabled: bool) -> Self {
70        self.game_config = self.game_config.with_fullscreen(enabled);
71        self
72    }
73
74    /// Sets the target frames per second (0 = unlimited).
75    pub fn with_target_fps(mut self, fps: u32) -> Self {
76        self.game_config = self.game_config.with_target_fps(fps);
77        self
78    }
79
80    /// Enables or disables the FPS stats overlay.
81    pub fn with_fps_overlay(mut self, enabled: bool) -> Self {
82        self.game_config = self.game_config.with_fps_overlay(enabled);
83        self
84    }
85
86    /// Replaces the entire [`GameConfig`] with the provided one.
87    pub fn with_game_config(mut self, config: GameConfig) -> Self {
88        self.game_config = config;
89        self
90    }
91
92    // Provider Selection (delegated to ProviderRegistryBuilder)
93
94    /// Sets the render provider.
95    pub fn with_render_provider(mut self, provider: impl RenderProvider + 'static) -> Self {
96        self.provider_builder = self.provider_builder.with_renderer(provider);
97        self
98    }
99
100    /// Sets the physics provider.
101    pub fn with_physics_provider(mut self, provider: impl PhysicsProvider + 'static) -> Self {
102        self.provider_builder = self.provider_builder.with_physics(provider);
103        self
104    }
105
106    /// Sets the audio provider.
107    pub fn with_audio_provider(mut self, provider: impl AudioProvider + 'static) -> Self {
108        self.provider_builder = self.provider_builder.with_audio(provider);
109        self
110    }
111
112    /// Sets the input provider.
113    pub fn with_input_provider(mut self, provider: impl InputProvider + 'static) -> Self {
114        self.provider_builder = self.provider_builder.with_input(provider);
115        self
116    }
117
118    // Build & Accessors
119
120    /// Consumes the builder and returns the `GameConfig` and `ProviderRegistry`.
121    pub fn build(self) -> (GameConfig, ProviderRegistry) {
122        (self.game_config, self.provider_builder.build())
123    }
124
125    /// Returns a reference to the current game configuration.
126    pub fn game_config(&self) -> &GameConfig {
127        &self.game_config
128    }
129
130    /// Returns a mutable reference to the current game configuration.
131    /// Used for direct field mutation in FFI boundary code.
132    pub(crate) fn game_config_mut(&mut self) -> &mut GameConfig {
133        &mut self.game_config
134    }
135}
136
137impl Default for EngineConfig {
138    fn default() -> Self {
139        Self::new()
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146    use crate::core::providers::impls::{
147        NullAudioProvider, NullInputProvider, NullPhysicsProvider, NullRenderProvider,
148    };
149
150    #[test]
151    fn test_engine_config_default() {
152        let config = EngineConfig::default();
153        let (game_config, providers) = config.build();
154        assert_eq!(game_config.title, "GoudEngine Game");
155        assert_eq!(game_config.width, 800);
156        assert_eq!(game_config.height, 600);
157        assert_eq!(providers.render.name(), "null");
158        assert_eq!(providers.physics.name(), "null");
159        assert_eq!(providers.audio.name(), "null");
160        assert_eq!(providers.input.name(), "null");
161    }
162
163    #[test]
164    fn test_engine_config_builder_chain() {
165        let config = EngineConfig::new()
166            .with_title("Chain Test")
167            .with_size(1920, 1080)
168            .with_vsync(false)
169            .with_fullscreen(true)
170            .with_target_fps(144)
171            .with_fps_overlay(true);
172        let gc = config.game_config();
173        assert_eq!(gc.title, "Chain Test");
174        assert_eq!(gc.width, 1920);
175        assert_eq!(gc.height, 1080);
176        assert!(!gc.vsync);
177        assert!(gc.fullscreen);
178        assert_eq!(gc.target_fps, 144);
179        assert!(gc.show_fps_overlay);
180    }
181
182    #[test]
183    fn test_engine_config_with_game_config() {
184        let gc = GameConfig::new("Custom", 640, 480)
185            .with_vsync(false)
186            .with_fullscreen(true);
187        let config = EngineConfig::new().with_game_config(gc);
188        let (game_config, _) = config.build();
189        assert_eq!(game_config.title, "Custom");
190        assert_eq!(game_config.width, 640);
191        assert_eq!(game_config.height, 480);
192        assert!(!game_config.vsync);
193        assert!(game_config.fullscreen);
194    }
195
196    #[test]
197    fn test_engine_config_custom_providers() {
198        let config = EngineConfig::new()
199            .with_render_provider(NullRenderProvider::new())
200            .with_physics_provider(NullPhysicsProvider::new())
201            .with_audio_provider(NullAudioProvider::new())
202            .with_input_provider(NullInputProvider::new());
203        let (_, providers) = config.build();
204        assert_eq!(providers.render.name(), "null");
205        assert_eq!(providers.physics.name(), "null");
206        assert_eq!(providers.audio.name(), "null");
207        assert_eq!(providers.input.name(), "null");
208    }
209
210    #[test]
211    fn test_engine_config_build_returns_parts() {
212        let config = EngineConfig::new()
213            .with_title("Parts Test")
214            .with_size(320, 240);
215        let (game_config, providers) = config.build();
216        assert_eq!(game_config.title, "Parts Test");
217        assert_eq!(game_config.width, 320);
218        assert_eq!(game_config.height, 240);
219        assert_eq!(providers.render.name(), "null");
220        assert_eq!(providers.physics.name(), "null");
221    }
222}