use serde::{Deserialize, Serialize}; use serde_json::Value; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub enum Hyper3dGenerationMode { TextToModel, ImageToModel, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Hyper3dTextToModelRequest { pub prompt: String, #[serde(default)] pub negative_prompt: Option, #[serde(default)] pub seed: Option, #[serde(default)] pub geometry_file_format: Option, #[serde(default)] pub material: Option, #[serde(default)] pub quality: Option, #[serde(default)] pub mesh_mode: Option, #[serde(default)] pub addons: Vec, #[serde(default)] pub bbox_condition: Option>, #[serde(default)] pub preview_render: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Hyper3dImageToModelRequest { #[serde(default)] pub image_data_urls: Vec, #[serde(default)] pub image_urls: Vec, #[serde(default)] pub prompt: Option, #[serde(default)] pub condition_mode: Option, #[serde(default)] pub seed: Option, #[serde(default)] pub geometry_file_format: Option, #[serde(default)] pub material: Option, #[serde(default)] pub quality: Option, #[serde(default)] pub mesh_mode: Option, #[serde(default)] pub addons: Vec, #[serde(default)] pub bbox_condition: Option>, #[serde(default)] pub preview_render: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Hyper3dTaskSubmitResponse { pub ok: bool, pub provider: String, pub mode: Hyper3dGenerationMode, pub task_uuid: String, pub subscription_key: String, pub job_uuids: Vec, pub message: Option, pub tier: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Hyper3dTaskStatusRequest { pub subscription_key: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Hyper3dTaskStatusResponse { pub ok: bool, pub provider: String, pub status: String, #[serde(default)] pub jobs: Vec, pub raw: Value, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Hyper3dJobStatusPayload { pub uuid: Option, pub status: String, pub progress: Option, pub message: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Hyper3dDownloadRequest { pub task_uuid: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Hyper3dDownloadResponse { pub ok: bool, pub provider: String, #[serde(default)] pub files: Vec, pub raw: Value, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Hyper3dDownloadFilePayload { pub name: String, pub url: String, } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn text_to_model_request_uses_camel_case_fields() { let payload = serde_json::to_value(Hyper3dTextToModelRequest { prompt: "低多边形宝箱".to_string(), negative_prompt: Some("文字".to_string()), seed: Some(42), geometry_file_format: Some("glb".to_string()), material: Some("PBR".to_string()), quality: Some("medium".to_string()), mesh_mode: Some("Quad".to_string()), addons: vec!["HighPack".to_string()], bbox_condition: Some(vec![1.0, 1.0, 1.0]), preview_render: Some(true), }) .expect("request should serialize"); assert_eq!(payload["geometryFileFormat"], json!("glb")); assert_eq!(payload["meshMode"], json!("Quad")); assert_eq!(payload["bboxCondition"], json!([1.0, 1.0, 1.0])); } #[test] fn submit_response_keeps_mode_as_kebab_case() { let payload = serde_json::to_value(Hyper3dTaskSubmitResponse { ok: true, provider: "hyper3d-rodin".to_string(), mode: Hyper3dGenerationMode::ImageToModel, task_uuid: "task-1".to_string(), subscription_key: "sub-1".to_string(), job_uuids: vec!["job-1".to_string()], message: Some("submitted".to_string()), tier: "Gen-2".to_string(), }) .expect("response should serialize"); assert_eq!(payload["mode"], json!("image-to-model")); assert_eq!(payload["subscriptionKey"], json!("sub-1")); } }