diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/component/image.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/component/image.rs index a037cb357..e22370f9d 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/component/image.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/component/image.rs @@ -6,7 +6,7 @@ use crate::ui_editor::resource::sprite::SpriteAsset; use crate::ui_editor::utils::SpriteAssetId; use nalgebra::Vector2; use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +use serde::{de::Deserializer, Deserialize, Serialize}; use ts_rs::TS; use typed_floats::tf32::StrictlyPositiveFinite; @@ -80,6 +80,15 @@ fn validate_fill_amount(amount: f32) -> Result<(), ComponentValueError> { Ok(()) } +fn deserialize_fill_amount<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let amount = f32::deserialize(deserializer)?; + validate_fill_amount(amount).map_err(serde::de::Error::custom)?; + Ok(amount) +} + #[derive(Clone, Copy, Debug, Deserialize, JsonSchema, PartialEq, Serialize, TS)] #[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] pub enum ImageType { @@ -101,6 +110,7 @@ pub enum ImageType { Filled { preserve_aspect: bool, method: FillMethod, + #[serde(deserialize_with = "deserialize_fill_amount")] amount: f32, }, } @@ -225,6 +235,42 @@ impl ImageComponent { } } +#[cfg(test)] +mod tests { + use super::{FillMethod, HorizontalFillOrigin, ImageType}; + + #[test] + fn filled_amount_is_validated_when_deserialized() { + let valid = serde_json::json!({ + "Filled": { + "preserve_aspect": false, + "method": { "Horizontal": "Left" }, + "amount": 0.5 + } + }); + let image_type: ImageType = serde_json::from_value(valid).expect("valid fill amount"); + assert!(matches!( + image_type, + ImageType::Filled { + method: FillMethod::Horizontal(HorizontalFillOrigin::Left), + amount: 0.5, + .. + } + )); + + for amount in ["-0.1", "1.1", "null"] { + let value = serde_json::json!({ + "Filled": { + "preserve_aspect": false, + "method": { "Horizontal": "Left" }, + "amount": serde_json::from_str::(amount).unwrap() + } + }); + assert!(serde_json::from_value::(value).is_err()); + } + } +} + impl Default for ImageComponent { fn default() -> Self { Self::new()