use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum PuzzleClearGenerationStatus { Draft, Generating, Ready, Failed, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum PuzzleClearActionType { CompileDraft, RegenerateAtlas, UpdateWorkMeta, UpdateBoardBackground, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum PuzzleClearRunStatus { Playing, LevelFailed, LevelCleared, Finished, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearImageAsset { pub asset_id: String, pub image_src: String, pub image_object_key: String, pub asset_object_id: String, pub generation_provider: String, pub prompt: String, pub width: u32, pub height: u32, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearPatternGroup { pub group_id: String, pub shape: String, pub width: u32, pub height: u32, pub atlas_x: u32, pub atlas_y: u32, pub atlas_width: u32, pub atlas_height: u32, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearCardAsset { pub card_id: String, pub group_id: String, pub shape: String, pub orientation: String, pub part_x: u32, pub part_y: u32, pub image_src: String, pub image_object_key: String, pub asset_object_id: String, pub source_atlas_cell: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearWorkspaceCreateRequest { pub template_id: String, pub work_title: String, pub work_description: String, pub theme_prompt: String, #[serde(default)] pub board_background_prompt: String, pub generate_board_background: bool, pub board_background_asset: Option, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearActionRequest { pub action_type: PuzzleClearActionType, pub profile_id: Option, pub work_title: Option, pub work_description: Option, pub theme_prompt: Option, #[serde(default)] pub board_background_prompt: Option, pub generate_board_background: Option, pub board_background_asset: Option, pub atlas_asset: Option, pub pattern_groups: Option>, pub card_assets: Option>, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearDraftResponse { pub template_id: String, pub template_name: String, pub profile_id: Option, pub work_title: String, pub work_description: String, pub theme_prompt: String, #[serde(default)] pub board_background_prompt: String, pub generate_board_background: bool, pub board_background_asset: Option, pub card_back_image_src: Option, pub atlas_asset: Option, pub pattern_groups: Vec, pub card_assets: Vec, pub generation_status: PuzzleClearGenerationStatus, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearSessionSnapshotResponse { pub session_id: String, pub owner_user_id: String, pub status: PuzzleClearGenerationStatus, pub draft: Option, pub created_at: String, pub updated_at: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearSessionResponse { pub session: PuzzleClearSessionSnapshotResponse, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearActionResponse { pub action_type: PuzzleClearActionType, pub session: PuzzleClearSessionSnapshotResponse, pub work: Option, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearWorkSummaryResponse { pub runtime_kind: String, pub work_id: String, pub profile_id: String, pub owner_user_id: String, pub source_session_id: Option, pub work_title: String, pub work_description: String, pub theme_prompt: String, pub cover_image_src: Option, pub publication_status: String, pub play_count: u32, pub updated_at: String, pub published_at: Option, pub publish_ready: bool, pub generation_status: PuzzleClearGenerationStatus, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearWorkProfileResponse { pub summary: PuzzleClearWorkSummaryResponse, pub draft: PuzzleClearDraftResponse, pub board_background_asset: Option, pub atlas_asset: PuzzleClearImageAsset, pub pattern_groups: Vec, pub card_assets: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearWorksResponse { pub items: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearWorkDetailResponse { pub item: PuzzleClearWorkProfileResponse, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearWorkMutationResponse { pub item: PuzzleClearWorkProfileResponse, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearBoardCell { pub row: u32, pub col: u32, pub card: Option, pub locked_group_id: Option, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearBoardSnapshot { pub rows: u32, pub cols: u32, pub cells: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearRuntimeSnapshotResponse { pub run_id: String, pub profile_id: String, pub owner_user_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub runtime_mode: Option, pub status: PuzzleClearRunStatus, pub level_index: u32, pub clears_done: u32, pub target_clears: u32, pub level_duration_seconds: u32, pub level_started_at_ms: u64, pub board: PuzzleClearBoardSnapshot, pub ready_columns: Vec>, pub started_at_ms: u64, pub finished_at_ms: Option, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearRunResponse { pub run: PuzzleClearRuntimeSnapshotResponse, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearStartRunRequest { pub profile_id: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearSwapRequest { pub from_row: u32, pub from_col: u32, pub to_row: u32, pub to_col: u32, pub client_action_id: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearRetryLevelRequest { pub client_action_id: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearNextLevelRequest { pub client_action_id: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PuzzleClearTimeUpRequest { pub client_action_id: String, } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn workspace_create_request_uses_camel_case() { let payload = PuzzleClearWorkspaceCreateRequest { template_id: "puzzle-clear".to_string(), work_title: "花园拼消消".to_string(), work_description: "轻松消除".to_string(), theme_prompt: "春日花园".to_string(), board_background_prompt: "樱花庭院".to_string(), generate_board_background: true, board_background_asset: None, }; let value = serde_json::to_value(payload).expect("request should serialize"); assert_eq!(value["templateId"], json!("puzzle-clear")); assert_eq!(value["themePrompt"], json!("春日花园")); assert_eq!(value["boardBackgroundPrompt"], json!("樱花庭院")); assert_eq!(value["generateBoardBackground"], json!(true)); } #[test] fn runtime_swap_request_uses_camel_case() { let payload = PuzzleClearSwapRequest { from_row: 1, from_col: 2, to_row: 1, to_col: 3, client_action_id: "swap-1".to_string(), }; let value = serde_json::to_value(payload).expect("request should serialize"); assert_eq!(value["fromRow"], json!(1)); assert_eq!(value["toCol"], json!(3)); assert_eq!(value["clientActionId"], json!("swap-1")); } }