use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub enum JumpHopDifficulty { Easy, Standard, Advanced, Challenge, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub enum JumpHopStylePreset { MinimalBlocks, PaperToy, NeonGlass, ForestStone, FutureMetal, Custom, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub enum JumpHopGenerationStatus { Draft, Generating, Ready, Failed, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub enum JumpHopTileType { Start, Normal, Target, Finish, Bonus, Accent, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub enum JumpHopActionType { CompileDraft, RegenerateCharacter, RegenerateTiles, UpdateWorkMeta, UpdateDifficulty, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub enum JumpHopRunStatus { Playing, Failed, Cleared, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub enum JumpHopJumpResult { Miss, Hit, Perfect, Finish, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopWorkspaceCreateRequest { pub template_id: String, pub work_title: String, pub work_description: String, pub theme_tags: Vec, pub difficulty: JumpHopDifficulty, pub style_preset: JumpHopStylePreset, pub character_prompt: String, pub tile_prompt: String, #[serde(default)] pub end_mood_prompt: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopActionRequest { pub action_type: JumpHopActionType, #[serde(default)] pub work_title: Option, #[serde(default)] pub work_description: Option, #[serde(default)] pub theme_tags: Option>, #[serde(default)] pub difficulty: Option, #[serde(default)] pub style_preset: Option, #[serde(default)] pub character_prompt: Option, #[serde(default)] pub tile_prompt: Option, #[serde(default)] pub end_mood_prompt: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopCharacterAsset { 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, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopTileAsset { pub tile_type: JumpHopTileType, pub image_src: String, pub image_object_key: String, pub asset_object_id: String, pub source_atlas_cell: String, pub visual_width: u32, pub visual_height: u32, pub top_surface_radius: f32, pub landing_radius: f32, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopScoring { pub charge_to_distance_ratio: f32, pub max_charge_ms: u32, pub hit_bonus: u32, pub perfect_bonus: u32, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopPlatform { pub platform_id: String, pub tile_type: JumpHopTileType, pub x: f32, pub y: f32, pub width: f32, pub height: f32, pub landing_radius: f32, pub perfect_radius: f32, pub score_value: u32, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopPath { pub seed: String, pub difficulty: JumpHopDifficulty, pub platforms: Vec, pub finish_index: u32, pub camera_preset: String, pub scoring: JumpHopScoring, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopLastJump { pub charge_ms: u32, pub jump_distance: f32, pub target_platform_index: u32, pub landed_x: f32, pub landed_y: f32, pub result: JumpHopJumpResult, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopDraftResponse { pub template_id: String, pub template_name: String, #[serde(default)] pub profile_id: Option, pub work_title: String, pub work_description: String, pub theme_tags: Vec, pub difficulty: JumpHopDifficulty, pub style_preset: JumpHopStylePreset, pub character_prompt: String, pub tile_prompt: String, #[serde(default)] pub end_mood_prompt: Option, #[serde(default)] pub character_asset: Option, #[serde(default)] pub tile_atlas_asset: Option, #[serde(default)] pub tile_assets: Vec, #[serde(default)] pub path: Option, #[serde(default)] pub cover_composite: Option, pub generation_status: JumpHopGenerationStatus, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopSessionSnapshotResponse { pub session_id: String, pub owner_user_id: String, pub status: JumpHopGenerationStatus, #[serde(default)] pub draft: Option, pub created_at: String, pub updated_at: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopSessionResponse { pub session: JumpHopSessionSnapshotResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopActionResponse { pub action_type: JumpHopActionType, pub session: JumpHopSessionSnapshotResponse, #[serde(default)] pub work: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopWorkSummaryResponse { pub runtime_kind: String, pub work_id: String, pub profile_id: String, pub owner_user_id: String, #[serde(default)] pub source_session_id: Option, pub work_title: String, pub work_description: String, pub theme_tags: Vec, pub difficulty: JumpHopDifficulty, pub style_preset: JumpHopStylePreset, #[serde(default)] pub cover_image_src: Option, pub publication_status: String, pub play_count: u32, pub updated_at: String, #[serde(default)] pub published_at: Option, pub publish_ready: bool, pub generation_status: JumpHopGenerationStatus, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopWorkProfileResponse { #[serde(flatten)] pub summary: JumpHopWorkSummaryResponse, pub draft: JumpHopDraftResponse, pub path: JumpHopPath, pub character_asset: JumpHopCharacterAsset, pub tile_atlas_asset: JumpHopCharacterAsset, pub tile_assets: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopWorksResponse { pub items: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopWorkDetailResponse { pub item: JumpHopWorkProfileResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopWorkMutationResponse { pub item: JumpHopWorkProfileResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopGalleryCardResponse { pub public_work_code: String, pub work_id: String, pub profile_id: String, pub owner_user_id: String, pub author_display_name: String, pub work_title: String, pub work_description: String, #[serde(default)] pub cover_image_src: Option, pub theme_tags: Vec, pub difficulty: JumpHopDifficulty, pub style_preset: JumpHopStylePreset, pub publication_status: String, pub play_count: u32, pub updated_at: String, #[serde(default)] pub published_at: Option, pub generation_status: JumpHopGenerationStatus, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopGalleryResponse { pub items: Vec, pub has_more: bool, #[serde(default)] pub next_cursor: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopGalleryDetailResponse { pub item: JumpHopWorkProfileResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopRuntimeRunSnapshotResponse { pub run_id: String, pub profile_id: String, pub owner_user_id: String, pub status: JumpHopRunStatus, pub current_platform_index: u32, pub score: u32, pub combo: u32, pub path: JumpHopPath, #[serde(default)] pub last_jump: Option, pub started_at_ms: u64, #[serde(default)] pub finished_at_ms: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopRunResponse { pub run: JumpHopRuntimeRunSnapshotResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopStartRunRequest { pub profile_id: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopJumpRequest { pub charge_ms: u32, pub client_event_id: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopRestartRunRequest { pub client_action_id: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct JumpHopJumpResponse { pub run: JumpHopRuntimeRunSnapshotResponse, } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn jump_hop_workspace_request_uses_camel_case() { let payload = serde_json::to_value(JumpHopWorkspaceCreateRequest { template_id: "jump-hop".to_string(), work_title: "跳一跳".to_string(), work_description: "俯视角跳跃闯关".to_string(), theme_tags: vec!["休闲".to_string()], difficulty: JumpHopDifficulty::Easy, style_preset: JumpHopStylePreset::MinimalBlocks, character_prompt: "角色".to_string(), tile_prompt: "地块".to_string(), end_mood_prompt: None, }) .expect("payload should serialize"); assert_eq!(payload["templateId"], json!("jump-hop")); assert_eq!(payload["difficulty"], json!("easy")); assert_eq!(payload["stylePreset"], json!("minimal-blocks")); } }