use serde::{Deserialize, Serialize}; use crate::creation_audio::CreationAudioAsset; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PutMatch3DWorkRequest { pub game_name: String, #[serde(default)] pub theme_text: Option, pub summary: String, pub tags: Vec, #[serde(default)] pub cover_image_src: Option, #[serde(default)] pub reference_image_src: Option, pub clear_count: u32, pub difficulty: u32, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GenerateMatch3DWorkTagsRequest { pub game_name: String, pub theme_text: String, #[serde(default)] pub summary: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GenerateMatch3DWorkTagsResponse { pub tags: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PutMatch3DAudioAssetsRequest { pub generated_item_assets: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PersistMatch3DGeneratedModelRequest { pub item_id: String, pub item_name: String, pub source_url: String, #[serde(default)] pub file_name: Option, #[serde(default)] pub task_uuid: Option, #[serde(default)] pub subscription_key: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PersistMatch3DGeneratedModelResponse { pub asset: Match3DGeneratedItemAssetResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GenerateMatch3DCoverImageRequest { pub prompt: String, #[serde(default)] pub uploaded_image_src: Option, #[serde(default)] pub reference_image_src: Option, #[serde(default)] pub reference_image_srcs: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GenerateMatch3DCoverImageResponse { pub item: Match3DWorkProfileResponse, pub cover_image_src: String, pub cover_image_object_key: String, pub prompt: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GenerateMatch3DBackgroundImageRequest { pub prompt: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GenerateMatch3DBackgroundImageResponse { pub item: Match3DWorkProfileResponse, pub background_image_src: String, pub background_image_object_key: String, pub generated_background_asset: Match3DGeneratedBackgroundAssetResponse, pub prompt: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GenerateMatch3DContainerImageRequest { pub prompt: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GenerateMatch3DContainerImageResponse { pub item: Match3DWorkProfileResponse, pub container_image_src: String, pub container_image_object_key: String, pub generated_background_asset: Match3DGeneratedBackgroundAssetResponse, pub prompt: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GenerateMatch3DItemAssetsRequest { pub item_names: Vec, #[serde(default)] pub mode: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GenerateMatch3DItemAssetsResponse { pub item: Match3DWorkProfileResponse, pub generated_item_assets: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DWorkSummaryResponse { pub work_id: String, pub profile_id: String, pub owner_user_id: String, #[serde(default)] pub source_session_id: Option, pub game_name: String, pub theme_text: String, pub summary: String, pub tags: Vec, #[serde(default)] pub cover_image_src: Option, #[serde(default)] pub reference_image_src: Option, pub clear_count: u32, pub difficulty: u32, pub publication_status: String, pub play_count: u32, pub updated_at: String, #[serde(default)] pub published_at: Option, pub publish_ready: bool, #[serde(default)] pub generation_status: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub background_prompt: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub background_image_src: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub background_image_object_key: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub generated_background_asset: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub generated_item_assets: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DGeneratedBackgroundAssetResponse { pub prompt: String, #[serde(default)] pub image_src: Option, #[serde(default)] pub image_object_key: Option, #[serde(default)] pub container_prompt: Option, #[serde(default)] pub container_image_src: Option, #[serde(default)] pub container_image_object_key: Option, pub status: String, #[serde(default)] pub error: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DGeneratedItemImageViewResponse { pub view_id: String, pub view_index: u32, #[serde(default)] pub image_src: Option, #[serde(default)] pub image_object_key: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DGeneratedItemAssetResponse { pub item_id: String, pub item_name: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub item_size: Option, #[serde(default)] pub image_src: Option, #[serde(default)] pub image_object_key: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub image_views: Vec, #[serde(default)] pub model_src: Option, #[serde(default)] pub model_object_key: Option, #[serde(default)] pub model_file_name: Option, #[serde(default)] pub task_uuid: Option, #[serde(default)] pub subscription_key: Option, #[serde(default)] pub sound_prompt: Option, #[serde(default)] pub background_music_title: Option, #[serde(default)] pub background_music_style: Option, #[serde(default)] pub background_music_prompt: Option, #[serde(default)] pub background_music: Option, #[serde(default)] pub click_sound: Option, #[serde(default)] pub background_asset: Option, pub status: String, #[serde(default)] pub error: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DWorkProfileResponse { #[serde(flatten)] pub summary: Match3DWorkSummaryResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DWorksResponse { pub items: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DWorkDetailResponse { pub item: Match3DWorkProfileResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DWorkMutationResponse { pub item: Match3DWorkProfileResponse, } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn match3d_work_request_uses_camel_case() { let payload = serde_json::to_value(PutMatch3DWorkRequest { game_name: "水果抓大鹅".to_string(), theme_text: Some("水果".to_string()), summary: "水果主题".to_string(), tags: vec!["水果".to_string()], cover_image_src: None, reference_image_src: None, clear_count: 4, difficulty: 5, }) .expect("payload should serialize"); assert_eq!(payload["gameName"], json!("水果抓大鹅")); assert_eq!(payload["clearCount"], json!(4)); } #[test] fn match3d_work_summary_uses_camel_case_generation_status() { let payload = serde_json::to_value(Match3DWorkSummaryResponse { work_id: "work-1".to_string(), profile_id: "profile-1".to_string(), owner_user_id: "user-1".to_string(), source_session_id: Some("session-1".to_string()), game_name: "水果抓大鹅".to_string(), theme_text: "水果".to_string(), summary: "水果主题".to_string(), tags: vec!["水果".to_string()], cover_image_src: None, reference_image_src: None, clear_count: 4, difficulty: 5, publication_status: "draft".to_string(), play_count: 0, updated_at: "2026-05-01T00:00:00Z".to_string(), published_at: None, publish_ready: false, generation_status: Some("generating".to_string()), background_prompt: None, background_image_src: None, background_image_object_key: None, generated_background_asset: None, generated_item_assets: Vec::new(), }) .expect("payload should serialize"); assert_eq!(payload["generationStatus"], json!("generating")); } }