use super::*; impl From for AssetEntityBindingInput { fn from(input: module_assets::AssetEntityBindingInput) -> Self { Self { binding_id: input.binding_id, asset_object_id: input.asset_object_id, entity_kind: input.entity_kind, entity_id: input.entity_id, slot: input.slot, asset_kind: input.asset_kind, owner_user_id: input.owner_user_id, profile_id: input.profile_id, updated_at_micros: input.updated_at_micros, } } } impl From for AssetObjectUpsertInput { fn from(input: module_assets::AssetObjectUpsertInput) -> Self { Self { asset_object_id: input.asset_object_id, bucket: input.bucket, object_key: input.object_key, access_policy: map_access_policy(input.access_policy), content_type: input.content_type, content_length: input.content_length, content_hash: input.content_hash, version: input.version, source_job_id: input.source_job_id, owner_user_id: input.owner_user_id, profile_id: input.profile_id, entity_id: input.entity_id, asset_kind: input.asset_kind, updated_at_micros: input.updated_at_micros, } } } impl From for AssetHistoryListInput { fn from(input: module_assets::AssetHistoryListInput) -> Self { Self { asset_kind: input.asset_kind, limit: input.limit, } } } impl From for CreationEntryTypeAdminUpsertInput { fn from(input: module_runtime::CreationEntryTypeAdminUpsertInput) -> Self { Self { id: input.id, title: input.title, subtitle: input.subtitle, badge: input.badge, image_src: input.image_src, visible: input.visible, open: input.open, sort_order: input.sort_order, } } } impl From for RuntimeSettingGetInput { fn from(input: module_runtime::RuntimeSettingGetInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeSettingUpsertInput { fn from(input: module_runtime::RuntimeSettingUpsertInput) -> Self { Self { user_id: input.user_id, music_volume: input.music_volume, platform_theme: map_runtime_platform_theme(input.platform_theme), updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeBrowseHistoryListInput { fn from(input: module_runtime::RuntimeBrowseHistoryListInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeBrowseHistoryClearInput { fn from(input: module_runtime::RuntimeBrowseHistoryClearInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeBrowseHistorySyncInput { fn from(input: module_runtime::RuntimeBrowseHistorySyncInput) -> Self { Self { user_id: input.user_id, entries: input.entries.into_iter().map(Into::into).collect(), updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeBrowseHistoryWriteInput { fn from(input: module_runtime::RuntimeBrowseHistoryWriteInput) -> Self { Self { owner_user_id: input.owner_user_id, profile_id: input.profile_id, world_name: input.world_name, subtitle: input.subtitle, summary_text: input.summary_text, cover_image_src: input.cover_image_src, theme_mode: input.theme_mode, author_display_name: input.author_display_name, visited_at: input.visited_at, } } } impl From for RuntimeProfileDashboardGetInput { fn from(input: module_runtime::RuntimeProfileDashboardGetInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeProfileWalletLedgerListInput { fn from(input: module_runtime::RuntimeProfileWalletLedgerListInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeProfileWalletAdjustmentInput { fn from(input: module_runtime::RuntimeProfileWalletAdjustmentInput) -> Self { Self { user_id: input.user_id, amount: input.amount, ledger_id: input.ledger_id, created_at_micros: input.created_at_micros, } } } impl From for RuntimeProfileRechargeCenterGetInput { fn from(input: module_runtime::RuntimeProfileRechargeCenterGetInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeProfileRechargeOrderGetInput { fn from(input: module_runtime::RuntimeProfileRechargeOrderGetInput) -> Self { Self { order_id: input.order_id, } } } impl From for RuntimeProfileRechargeOrderCreateInput { fn from(input: module_runtime::RuntimeProfileRechargeOrderCreateInput) -> Self { Self { user_id: input.user_id, product_id: input.product_id, payment_channel: input.payment_channel, created_at_micros: input.created_at_micros, } } } impl From for RuntimeProfileRechargeOrderPaidInput { fn from(input: module_runtime::RuntimeProfileRechargeOrderPaidInput) -> Self { Self { order_id: input.order_id, paid_at_micros: input.paid_at_micros, provider_transaction_id: input.provider_transaction_id, } } } impl From for RuntimeProfileFeedbackSubmissionInput { fn from(input: module_runtime::RuntimeProfileFeedbackSubmissionInput) -> Self { Self { user_id: input.user_id, description: input.description, contact_phone: input.contact_phone, evidence_items: input.evidence_items.into_iter().map(Into::into).collect(), created_at_micros: input.created_at_micros, } } } impl From for RuntimeProfileFeedbackEvidenceSnapshot { fn from(input: module_runtime::RuntimeProfileFeedbackEvidenceSnapshot) -> Self { Self { evidence_id: input.evidence_id, file_name: input.file_name, content_type: input.content_type, size_bytes: input.size_bytes, data_url: input.data_url, } } } impl From for RuntimeProfileRewardCodeRedeemInput { fn from(input: module_runtime::RuntimeProfileRewardCodeRedeemInput) -> Self { Self { user_id: input.user_id, code: input.code, redeemed_at_micros: input.redeemed_at_micros, } } } impl From for RuntimeProfileTaskCenterGetInput { fn from(input: module_runtime::RuntimeProfileTaskCenterGetInput) -> Self { Self { user_id: input.user_id, } } } impl From for AnalyticsMetricQueryInput { fn from(input: module_runtime::AnalyticsMetricQueryInput) -> Self { Self { event_key: input.event_key, scope_kind: map_runtime_tracking_scope_kind(input.scope_kind), scope_id: input.scope_id, granularity: map_analytics_granularity(input.granularity), } } } impl From for RuntimeProfileTaskClaimInput { fn from(input: module_runtime::RuntimeProfileTaskClaimInput) -> Self { Self { user_id: input.user_id, task_id: input.task_id, } } } impl From for RuntimeProfileTaskConfigAdminListInput { fn from(input: module_runtime::RuntimeProfileTaskConfigAdminListInput) -> Self { Self { admin_user_id: input.admin_user_id, } } } impl From for RuntimeProfileTaskConfigAdminUpsertInput { fn from(input: module_runtime::RuntimeProfileTaskConfigAdminUpsertInput) -> Self { Self { admin_user_id: input.admin_user_id, task_id: input.task_id, title: input.title, description: input.description, event_key: input.event_key, cycle: map_runtime_profile_task_cycle(input.cycle), scope_kind: map_runtime_tracking_scope_kind(input.scope_kind), threshold: input.threshold, reward_points: input.reward_points, enabled: input.enabled, sort_order: input.sort_order, updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeProfileTaskConfigAdminDisableInput { fn from(input: module_runtime::RuntimeProfileTaskConfigAdminDisableInput) -> Self { Self { admin_user_id: input.admin_user_id, task_id: input.task_id, updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeProfileRechargeProductAdminListInput { fn from(input: module_runtime::RuntimeProfileRechargeProductAdminListInput) -> Self { Self { admin_user_id: input.admin_user_id, } } } impl From for RuntimeProfileRechargeProductAdminUpsertInput { fn from(input: module_runtime::RuntimeProfileRechargeProductAdminUpsertInput) -> Self { Self { admin_user_id: input.admin_user_id, product_id: input.product_id, title: input.title, price_cents: input.price_cents, kind: map_runtime_profile_recharge_product_kind(input.kind), points_amount: input.points_amount, bonus_points: input.bonus_points, duration_days: input.duration_days, badge_label: input.badge_label, description: input.description, tier: map_runtime_profile_membership_tier(input.tier), enabled: input.enabled, sort_order: input.sort_order, updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeProfileRedeemCodeAdminUpsertInput { fn from(input: module_runtime::RuntimeProfileRedeemCodeAdminUpsertInput) -> Self { Self { admin_user_id: input.admin_user_id, code: input.code, mode: map_runtime_profile_redeem_code_mode(input.mode), reward_points: input.reward_points, max_uses: input.max_uses, enabled: input.enabled, allowed_user_ids: input.allowed_user_ids, allowed_public_user_codes: input.allowed_public_user_codes, updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeProfileRedeemCodeAdminDisableInput { fn from(input: module_runtime::RuntimeProfileRedeemCodeAdminDisableInput) -> Self { Self { admin_user_id: input.admin_user_id, code: input.code, updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeProfileRedeemCodeAdminListInput { fn from(input: module_runtime::RuntimeProfileRedeemCodeAdminListInput) -> Self { Self { admin_user_id: input.admin_user_id, } } } impl From for RuntimeProfileInviteCodeAdminUpsertInput { fn from(input: module_runtime::RuntimeProfileInviteCodeAdminUpsertInput) -> Self { Self { admin_user_id: input.admin_user_id, invite_code: input.invite_code, metadata_json: input.metadata_json, starts_at_micros: input.starts_at_micros, expires_at_micros: input.expires_at_micros, updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeProfileInviteCodeAdminListInput { fn from(input: module_runtime::RuntimeProfileInviteCodeAdminListInput) -> Self { Self { admin_user_id: input.admin_user_id, } } } impl From for RuntimeReferralInviteCenterGetInput { fn from(input: module_runtime::RuntimeReferralInviteCenterGetInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeReferralRedeemInput { fn from(input: module_runtime::RuntimeReferralRedeemInput) -> Self { Self { user_id: input.user_id, invite_code: input.invite_code, updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeProfilePlayStatsGetInput { fn from(input: module_runtime::RuntimeProfilePlayStatsGetInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeSnapshotGetInput { fn from(input: module_runtime::RuntimeSnapshotGetInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeSnapshotUpsertInput { fn from(input: module_runtime::RuntimeSnapshotUpsertInput) -> Self { Self { user_id: input.user_id, saved_at_micros: input.saved_at_micros, bottom_tab: input.bottom_tab, game_state_json: input.game_state_json, current_story_json: input.current_story_json, updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeSnapshotDeleteInput { fn from(input: module_runtime::RuntimeSnapshotDeleteInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeProfileSaveArchiveListInput { fn from(input: module_runtime::RuntimeProfileSaveArchiveListInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeProfileSaveArchiveResumeInput { fn from(input: module_runtime::RuntimeProfileSaveArchiveResumeInput) -> Self { Self { user_id: input.user_id, world_key: input.world_key, } } } impl From for AiTaskCreateInput { fn from(input: DomainAiTaskCreateInput) -> Self { Self { task_id: input.task_id, task_kind: map_ai_task_kind(input.task_kind), owner_user_id: input.owner_user_id, request_label: input.request_label, source_module: input.source_module, source_entity_id: input.source_entity_id, request_payload_json: input.request_payload_json, stages: input.stages.into_iter().map(Into::into).collect(), created_at_micros: input.created_at_micros, } } } impl From for AiTaskStartInput { fn from(input: DomainAiTaskStartInput) -> Self { Self { task_id: input.task_id, started_at_micros: input.started_at_micros, } } } impl From for AiTaskStageStartInput { fn from(input: DomainAiTaskStageStartInput) -> Self { Self { task_id: input.task_id, stage_kind: map_ai_task_stage_kind(input.stage_kind), started_at_micros: input.started_at_micros, } } } impl From for AiTextChunkAppendInput { fn from(input: DomainAiTextChunkAppendInput) -> Self { Self { task_id: input.task_id, stage_kind: map_ai_task_stage_kind(input.stage_kind), sequence: input.sequence, delta_text: input.delta_text, created_at_micros: input.created_at_micros, } } } impl From for AiStageCompletionInput { fn from(input: DomainAiStageCompletionInput) -> Self { Self { task_id: input.task_id, stage_kind: map_ai_task_stage_kind(input.stage_kind), text_output: input.text_output, structured_payload_json: input.structured_payload_json, warning_messages: input.warning_messages, completed_at_micros: input.completed_at_micros, } } } impl From for AiResultReferenceInput { fn from(input: DomainAiResultReferenceInput) -> Self { Self { task_id: input.task_id, reference_kind: map_ai_result_reference_kind(input.reference_kind), reference_id: input.reference_id, label: input.label, created_at_micros: input.created_at_micros, } } } impl From for AiTaskFinishInput { fn from(input: DomainAiTaskFinishInput) -> Self { Self { task_id: input.task_id, completed_at_micros: input.completed_at_micros, } } } impl From for AiTaskFailureInput { fn from(input: DomainAiTaskFailureInput) -> Self { Self { task_id: input.task_id, failure_message: input.failure_message, completed_at_micros: input.completed_at_micros, } } } impl From for AiTaskCancelInput { fn from(input: DomainAiTaskCancelInput) -> Self { Self { task_id: input.task_id, completed_at_micros: input.completed_at_micros, } } } impl From for AiTaskStageBlueprint { fn from(blueprint: DomainAiTaskStageBlueprint) -> Self { Self { stage_kind: map_ai_task_stage_kind(blueprint.stage_kind), label: blueprint.label, detail: blueprint.detail, order: blueprint.order, } } } impl From for CustomWorldProfileUpsertInput { fn from(input: CustomWorldProfileUpsertRecordInput) -> Self { Self { profile_id: input.profile_id, owner_user_id: input.owner_user_id, public_work_code: input.public_work_code, author_public_user_code: input.author_public_user_code, source_agent_session_id: input.source_agent_session_id, world_name: input.world_name, subtitle: input.subtitle, summary_text: input.summary_text, theme_mode: map_custom_world_theme_mode(input.theme_mode), cover_image_src: input.cover_image_src, profile_payload_json: input.profile_payload_json, playable_npc_count: input.playable_npc_count, landmark_count: input.landmark_count, author_display_name: input.author_display_name, updated_at_micros: input.updated_at_micros, } } } impl From for CustomWorldPublishWorldInput { fn from(input: CustomWorldPublishWorldRecordInput) -> Self { Self { session_id: input.session_id, profile_id: input.profile_id, owner_user_id: input.owner_user_id, public_work_code: input.public_work_code, author_public_user_code: input.author_public_user_code, draft_profile_json: input.draft_profile_json, legacy_result_profile_json: input.legacy_result_profile_json, setting_text: input.setting_text, author_display_name: input.author_display_name, published_at_micros: input.published_at_micros, } } } impl From for StorySessionInput { fn from(input: DomainStorySessionInput) -> Self { Self { story_session_id: input.story_session_id, runtime_session_id: input.runtime_session_id, actor_user_id: input.actor_user_id, world_profile_id: input.world_profile_id, initial_prompt: input.initial_prompt, opening_summary: input.opening_summary, created_at_micros: input.created_at_micros, } } } impl From for StoryContinueInput { fn from(input: DomainStoryContinueInput) -> Self { Self { story_session_id: input.story_session_id, event_id: input.event_id, narrative_text: input.narrative_text, choice_function_id: input.choice_function_id, updated_at_micros: input.updated_at_micros, } } } impl From for StorySessionStateInput { fn from(input: DomainStorySessionStateInput) -> Self { Self { story_session_id: input.story_session_id, } } } impl From for RuntimeInventoryStateQueryInput { fn from(input: DomainRuntimeInventoryStateQueryInput) -> Self { Self { runtime_session_id: input.runtime_session_id, actor_user_id: input.actor_user_id, } } } impl From for BattleStateQueryInput { fn from(input: DomainBattleStateQueryInput) -> Self { Self { battle_state_id: input.battle_state_id, } } } impl From for BattleStateInput { fn from(input: DomainBattleStateInput) -> Self { Self { battle_state_id: input.battle_state_id, story_session_id: input.story_session_id, runtime_session_id: input.runtime_session_id, actor_user_id: input.actor_user_id, chapter_id: input.chapter_id, target_npc_id: input.target_npc_id, target_name: input.target_name, battle_mode: map_battle_mode(input.battle_mode), player_hp: input.player_hp, player_max_hp: input.player_max_hp, player_mana: input.player_mana, player_max_mana: input.player_max_mana, target_hp: input.target_hp, target_max_hp: input.target_max_hp, experience_reward: input.experience_reward, reward_items: input .reward_items .into_iter() .map(map_runtime_item_reward_item_snapshot) .collect(), created_at_micros: input.created_at_micros, } } } impl From for ResolveCombatActionInput { fn from(input: DomainResolveCombatActionInput) -> Self { Self { battle_state_id: input.battle_state_id, function_id: input.function_id, action_text: input.action_text, base_damage: input.base_damage, mana_cost: input.mana_cost, heal: input.heal, mana_restore: input.mana_restore, counter_multiplier_basis_points: input.counter_multiplier_basis_points, updated_at_micros: input.updated_at_micros, } } } pub(crate) fn map_procedure_result( result: AssetObjectProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("对象快照"))?; Ok(build_asset_object_record(map_snapshot(snapshot))) } pub(crate) fn map_entity_binding_procedure_result( result: AssetEntityBindingProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("绑定快照"))?; Ok(build_asset_entity_binding_record( map_entity_binding_snapshot(snapshot), )) } pub(crate) fn map_asset_history_list_result( result: AssetHistoryListResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(result .entries .into_iter() .map(map_asset_history_entry_snapshot) .map(build_asset_history_entry_record) .collect()) } pub type BarkBattleDraftConfigRecord = serde_json::Value; pub type BarkBattleRuntimeConfigRecord = serde_json::Value; pub type BarkBattleRunRecord = serde_json::Value; pub(crate) fn map_bark_battle_draft_config_procedure_result( result: BarkBattleProcedureResult, ) -> Result { parse_bark_battle_row_json(result, "Bark Battle draft config") } pub(crate) fn map_bark_battle_runtime_config_procedure_result( result: BarkBattleProcedureResult, ) -> Result { parse_bark_battle_row_json(result, "Bark Battle runtime config") } pub(crate) fn map_bark_battle_run_procedure_result( result: BarkBattleProcedureResult, ) -> Result { parse_bark_battle_row_json(result, "Bark Battle run") } fn parse_bark_battle_row_json( result: BarkBattleProcedureResult, label: &'static str, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let row_json = result .row_json .ok_or_else(|| SpacetimeClientError::missing_snapshot(label))?; serde_json::from_str(&row_json) .map_err(|error| SpacetimeClientError::Runtime(format!("{label} JSON 解析失败: {error}"))) } pub type CreationEntryConfigRecord = shared_contracts::creation_entry_config::CreationEntryConfigResponse; pub(crate) fn map_creation_entry_config_procedure_result( result: CreationEntryConfigProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("创作入口配置快照"))?; Ok(module_runtime::build_creation_entry_config_response( map_creation_entry_config_snapshot(snapshot), )) } fn map_creation_entry_config_snapshot( snapshot: CreationEntryConfigSnapshot, ) -> module_runtime::CreationEntryConfigSnapshot { module_runtime::CreationEntryConfigSnapshot { config_id: snapshot.config_id, start_card: module_runtime::CreationEntryStartCardSnapshot { title: snapshot.start_card.title, description: snapshot.start_card.description, idle_badge: snapshot.start_card.idle_badge, busy_badge: snapshot.start_card.busy_badge, }, type_modal: module_runtime::CreationEntryTypeModalSnapshot { title: snapshot.type_modal.title, description: snapshot.type_modal.description, }, creation_types: snapshot .creation_types .into_iter() .map(|item| module_runtime::CreationEntryTypeSnapshot { id: item.id, title: item.title, subtitle: item.subtitle, badge: item.badge, image_src: item.image_src, visible: item.visible, open: item.open, sort_order: item.sort_order, updated_at_micros: item.updated_at_micros, }) .collect(), updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_setting_procedure_result( result: RuntimeSettingProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("runtime settings 快照"))?; Ok(build_runtime_setting_record(map_runtime_setting_snapshot( snapshot, ))) } pub(crate) fn map_auth_store_snapshot_procedure_result( result: AuthStoreSnapshotProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let record = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("认证快照"))?; Ok(map_auth_store_snapshot_record(record)) } pub(crate) fn map_auth_store_snapshot_record( record: crate::module_bindings::AuthStoreSnapshotRecord, ) -> crate::AuthStoreSnapshotRecord { crate::AuthStoreSnapshotRecord { snapshot_json: record.snapshot_json, updated_at_micros: record.updated_at_micros, } } pub(crate) fn map_auth_store_snapshot_import_procedure_result( result: AuthStoreSnapshotImportProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let record = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("认证快照导入结果"))?; Ok(AuthStoreSnapshotImportRecord { imported_user_count: record.imported_user_count, imported_identity_count: record.imported_identity_count, imported_refresh_session_count: record.imported_refresh_session_count, }) } pub(crate) fn map_runtime_browse_history_procedure_result( result: RuntimeBrowseHistoryProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(result .entries .into_iter() .map(|snapshot| { build_runtime_browse_history_record(map_runtime_browse_history_snapshot(snapshot)) }) .collect()) } pub(crate) fn map_runtime_profile_dashboard_procedure_result( result: RuntimeProfileDashboardProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile dashboard 快照"))?; Ok(build_runtime_profile_dashboard_record( map_runtime_profile_dashboard_snapshot(snapshot), )) } pub(crate) fn map_runtime_profile_wallet_ledger_procedure_result( result: RuntimeProfileWalletLedgerProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(result .entries .into_iter() .map(|snapshot| { build_runtime_profile_wallet_ledger_entry_record( map_runtime_profile_wallet_ledger_entry_snapshot(snapshot), ) }) .collect()) } pub(crate) fn map_runtime_profile_wallet_adjustment_procedure_result( result: RuntimeProfileWalletAdjustmentProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile dashboard 快照"))?; Ok(build_runtime_profile_dashboard_record( map_runtime_profile_dashboard_snapshot(snapshot), )) } pub(crate) fn map_runtime_profile_recharge_center_procedure_result( result: RuntimeProfileRechargeCenterProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile recharge center 快照"))?; Ok(build_runtime_profile_recharge_center_record( map_runtime_profile_recharge_center_snapshot(snapshot), )) } pub(crate) fn map_runtime_profile_recharge_order_procedure_result( result: RuntimeProfileRechargeCenterProcedureResult, ) -> Result< ( RuntimeProfileRechargeCenterRecord, RuntimeProfileRechargeOrderRecord, ), SpacetimeClientError, > { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let center = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile recharge center 快照"))?; let order = result .order .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile recharge order 快照"))?; Ok(( build_runtime_profile_recharge_center_record(map_runtime_profile_recharge_center_snapshot( center, )), module_runtime::build_runtime_profile_recharge_order_record( map_runtime_profile_recharge_order_snapshot(order), ), )) } pub(crate) fn map_runtime_profile_feedback_submission_procedure_result( result: RuntimeProfileFeedbackSubmissionProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile feedback 快照"))?; build_runtime_profile_feedback_submission_record( map_runtime_profile_feedback_submission_snapshot(snapshot), ) .map_err(SpacetimeClientError::validation_failed) } pub(crate) fn map_runtime_referral_invite_center_procedure_result( result: RuntimeReferralInviteCenterProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("referral invite center 快照"))?; Ok(build_runtime_referral_invite_center_record( map_runtime_referral_invite_center_snapshot(snapshot), )) } pub(crate) fn map_runtime_referral_redeem_procedure_result( result: RuntimeReferralRedeemProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("referral redeem 快照"))?; Ok(build_runtime_referral_redeem_record( map_runtime_referral_redeem_snapshot(snapshot), )) } pub(crate) fn map_runtime_profile_reward_code_redeem_procedure_result( result: RuntimeProfileRewardCodeRedeemProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("reward redeem 快照"))?; Ok(build_runtime_profile_reward_code_redeem_record( map_runtime_profile_reward_code_redeem_snapshot(snapshot), )) } pub(crate) fn map_runtime_tracking_event_procedure_result( result: RuntimeTrackingEventProcedureResult, ) -> Result<(), SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(()) } pub(crate) fn map_runtime_profile_task_center_procedure_result( result: RuntimeProfileTaskCenterProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile task center 快照"))?; Ok(build_runtime_profile_task_center_record( map_runtime_profile_task_center_snapshot(snapshot), )) } pub(crate) fn map_analytics_metric_query_procedure_result( result: AnalyticsMetricQueryProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(DomainAnalyticsMetricQueryResponse { buckets: result .buckets .into_iter() .map(map_analytics_bucket_metric) .collect(), }) } pub(crate) fn map_runtime_profile_task_claim_procedure_result( result: RuntimeProfileTaskClaimProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile task claim 快照"))?; Ok(build_runtime_profile_task_claim_record( map_runtime_profile_task_claim_snapshot(snapshot), )) } pub(crate) fn map_runtime_profile_task_config_admin_list_procedure_result( result: RuntimeProfileTaskConfigAdminListProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(result .entries .into_iter() .map(|snapshot| { build_runtime_profile_task_config_record(map_runtime_profile_task_config_snapshot( snapshot, )) }) .collect()) } pub(crate) fn map_runtime_profile_task_config_admin_procedure_result( result: RuntimeProfileTaskConfigAdminProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile task config 快照"))?; Ok(build_runtime_profile_task_config_record( map_runtime_profile_task_config_snapshot(snapshot), )) } pub(crate) fn map_runtime_profile_recharge_product_admin_list_procedure_result( result: RuntimeProfileRechargeProductAdminListProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(result .entries .into_iter() .map(|snapshot| { build_runtime_profile_recharge_product_config_record( map_runtime_profile_recharge_product_config_snapshot(snapshot), ) }) .collect()) } pub(crate) fn map_runtime_profile_recharge_product_admin_procedure_result( result: RuntimeProfileRechargeProductAdminProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("recharge product config 快照"))?; Ok(build_runtime_profile_recharge_product_config_record( map_runtime_profile_recharge_product_config_snapshot(snapshot), )) } pub(crate) fn map_runtime_profile_redeem_code_admin_procedure_result( result: RuntimeProfileRedeemCodeAdminProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("redeem code 快照"))?; Ok(build_runtime_profile_redeem_code_record( map_runtime_profile_redeem_code_snapshot(snapshot), )) } pub(crate) fn map_runtime_profile_redeem_code_admin_list_procedure_result( result: RuntimeProfileRedeemCodeAdminListProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(result .entries .into_iter() .map(|snapshot| { build_runtime_profile_redeem_code_record(map_runtime_profile_redeem_code_snapshot( snapshot, )) }) .collect()) } pub(crate) fn map_runtime_profile_invite_code_admin_procedure_result( result: RuntimeProfileInviteCodeAdminProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::Procedure( result .error_message .unwrap_or_else(|| "SpacetimeDB procedure 返回未知错误".to_string()), )); } let snapshot = result.record.ok_or_else(|| { SpacetimeClientError::Procedure("SpacetimeDB procedure 未返回 invite code 快照".to_string()) })?; Ok(build_runtime_profile_invite_code_record( map_runtime_profile_invite_code_snapshot(snapshot), )) } pub(crate) fn map_runtime_profile_invite_code_admin_list_procedure_result( result: RuntimeProfileInviteCodeAdminListProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(result .entries .into_iter() .map(|snapshot| { build_runtime_profile_invite_code_record(map_runtime_profile_invite_code_snapshot( snapshot, )) }) .collect()) } pub(crate) fn map_runtime_profile_play_stats_procedure_result( result: RuntimeProfilePlayStatsProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile play stats 快照"))?; Ok(build_runtime_profile_play_stats_record( map_runtime_profile_play_stats_snapshot(snapshot), )) } pub(crate) fn map_runtime_snapshot_procedure_result( result: RuntimeSnapshotProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } result .record .map(|snapshot| { build_runtime_snapshot_record(map_runtime_snapshot_snapshot(snapshot)) .map_err(|error| SpacetimeClientError::Runtime(error.to_string())) }) .transpose() } pub(crate) fn map_runtime_snapshot_required_procedure_result( result: RuntimeSnapshotProcedureResult, ) -> Result { map_runtime_snapshot_procedure_result(result)? .ok_or_else(|| SpacetimeClientError::missing_snapshot("runtime snapshot 快照")) } pub(crate) fn map_runtime_snapshot_delete_procedure_result( result: RuntimeSnapshotProcedureResult, ) -> Result { map_runtime_snapshot_procedure_result(result).map(|record| record.is_some()) } pub(crate) fn map_runtime_profile_save_archive_list_procedure_result( result: RuntimeProfileSaveArchiveProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } result .entries .into_iter() .map(|snapshot| { build_runtime_profile_save_archive_record(map_runtime_profile_save_archive_snapshot( snapshot, )) .map_err(|error| SpacetimeClientError::Runtime(error.to_string())) }) .collect() } pub(crate) fn map_runtime_profile_save_archive_resume_procedure_result( result: RuntimeProfileSaveArchiveProcedureResult, ) -> Result<(RuntimeProfileSaveArchiveRecord, RuntimeSnapshotRecord), SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let archive = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("save archive 快照"))?; let snapshot = result .current_snapshot .ok_or_else(|| SpacetimeClientError::missing_snapshot("恢复后的 runtime snapshot"))?; Ok(( build_runtime_profile_save_archive_record(map_runtime_profile_save_archive_snapshot( archive, )) .map_err(|error| SpacetimeClientError::Runtime(error.to_string()))?, build_runtime_snapshot_record(map_runtime_snapshot_snapshot(snapshot)) .map_err(|error| SpacetimeClientError::Runtime(error.to_string()))?, )) } pub(crate) fn map_ai_task_procedure_result( result: AiTaskProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let task = result .task .ok_or_else(|| SpacetimeClientError::missing_snapshot("ai_task 快照"))?; Ok(AiTaskMutationRecord { task: map_ai_task_snapshot(task), text_chunk: result.text_chunk.map(map_ai_text_chunk_snapshot), }) } pub(crate) fn map_custom_world_profile_list_result( result: CustomWorldProfileListResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } result .entries .into_iter() .map(map_custom_world_library_entry_from_profile_snapshot) .collect() } pub(crate) fn map_custom_world_library_detail_result( result: CustomWorldLibraryMutationResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let entry = result .entry .ok_or_else(|| SpacetimeClientError::Procedure("custom_world_profile 不存在".to_string())) .and_then(map_custom_world_library_entry_from_profile_snapshot)?; let gallery_entry = result .gallery_entry .map(map_custom_world_gallery_entry_snapshot) .transpose()?; Ok(CustomWorldLibraryMutationRecord { entry, gallery_entry, }) } pub(crate) fn map_custom_world_gallery_list_result( result: CustomWorldGalleryListResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(result .entries .into_iter() .map(map_custom_world_gallery_entry_snapshot) .collect::, _>>()?) } pub(crate) fn map_custom_world_library_mutation_result( result: CustomWorldLibraryMutationResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let entry = result .entry .ok_or_else(|| SpacetimeClientError::missing_snapshot("custom world entry")) .and_then(map_custom_world_library_entry_from_profile_snapshot)?; let gallery_entry = result .gallery_entry .map(map_custom_world_gallery_entry_snapshot) .transpose()?; Ok(CustomWorldLibraryMutationRecord { entry, gallery_entry, }) } pub(crate) fn map_custom_world_publish_world_result( result: CustomWorldPublishWorldResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let compiled_record = result .compiled_record .ok_or_else(|| SpacetimeClientError::missing_snapshot("published profile compile 快照")) .and_then(map_custom_world_published_profile_compile_snapshot)?; let entry = result .entry .ok_or_else(|| SpacetimeClientError::missing_snapshot("custom world entry")) .and_then(map_custom_world_library_entry_from_profile_snapshot)?; let gallery_entry = result .gallery_entry .map(map_custom_world_gallery_entry_snapshot) .transpose()?; let session_stage = result .session_stage .ok_or_else(|| SpacetimeClientError::missing_snapshot("session stage")) .map(map_rpg_agent_stage)?; Ok(CustomWorldPublishWorldRecord { compiled_record, entry, gallery_entry, session_stage, }) } pub(crate) fn map_custom_world_agent_session_procedure_result( result: CustomWorldAgentSessionProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let session = result .session .ok_or_else(|| SpacetimeClientError::missing_snapshot("custom world agent session 快照"))?; map_custom_world_agent_session_snapshot(session) } pub(crate) fn map_custom_world_agent_operation_procedure_result( result: CustomWorldAgentOperationProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let operation = result.operation.ok_or_else(|| { SpacetimeClientError::missing_snapshot("custom world agent operation 快照") })?; Ok(map_custom_world_agent_operation_snapshot(operation)) } pub(crate) fn map_custom_world_works_list_result( result: CustomWorldWorksListResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } result .items .into_iter() .map(map_custom_world_work_summary_snapshot) .collect() } pub(crate) fn map_custom_world_draft_card_detail_result( result: CustomWorldDraftCardDetailResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let card = result .card .ok_or_else(|| SpacetimeClientError::missing_snapshot("custom world card detail 快照"))?; map_custom_world_draft_card_detail_snapshot(card) } pub(crate) fn map_custom_world_agent_action_execute_result( result: CustomWorldAgentActionExecuteResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let operation = result.operation.ok_or_else(|| { SpacetimeClientError::missing_snapshot("custom world action operation 快照") })?; Ok(CustomWorldAgentActionExecuteRecord { operation: map_custom_world_agent_operation_snapshot(operation), }) } pub(crate) fn map_puzzle_agent_session_procedure_result( result: PuzzleAgentSessionProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let session_json = result .session_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("puzzle agent session 快照"))?; let session: DomainPuzzleAgentSessionSnapshot = serde_json::from_str(&session_json).map_err(|error| { SpacetimeClientError::Runtime(format!("puzzle agent session_json 非法: {error}")) })?; Ok(map_puzzle_agent_session_snapshot(session)) } pub(crate) fn map_puzzle_work_procedure_result( result: PuzzleWorkProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let item_json = result .item_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("puzzle work 快照"))?; let item: DomainPuzzleWorkProfile = serde_json::from_str(&item_json).map_err(|error| { SpacetimeClientError::Runtime(format!("puzzle work item_json 非法: {error}")) })?; Ok(map_puzzle_work_profile(item)) } pub(crate) fn map_puzzle_works_procedure_result( result: PuzzleWorksProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let items_json = result .items_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("puzzle works 快照"))?; let items: Vec = serde_json::from_str(&items_json).map_err(|error| { SpacetimeClientError::Runtime(format!("puzzle works items_json 非法: {error}")) })?; Ok(items.into_iter().map(map_puzzle_work_profile).collect()) } pub(crate) fn map_puzzle_run_procedure_result( result: PuzzleRunProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let run_json = result .run_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("puzzle run 快照"))?; let run: DomainPuzzleRunSnapshot = serde_json::from_str(&run_json).map_err(|error| { SpacetimeClientError::Runtime(format!("puzzle run run_json 非法: {error}")) })?; Ok(map_puzzle_run_snapshot(run)) } pub(crate) fn map_big_fish_session_procedure_result( result: BigFishSessionProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let session = result .session .ok_or_else(|| SpacetimeClientError::missing_snapshot("big fish session 快照"))?; Ok(map_big_fish_session_snapshot(session)) } pub(crate) fn map_big_fish_works_procedure_result( result: BigFishWorksProcedureResult, fallback_owner_user_id: Option<&str>, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let items_json = result .items_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("big fish works 快照"))?; serde_json::from_str::>(&items_json) .map(|items| { items .into_iter() .map(|item| item.into_record(fallback_owner_user_id)) .collect() }) .map_err(|error| { SpacetimeClientError::Runtime(format!("big fish works items_json 非法: {error}")) }) } pub(crate) fn map_big_fish_run_procedure_result( result: BigFishRunProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let run_json = result .run_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("big fish run 快照"))?; let run: module_big_fish::BigFishRuntimeSnapshot = serde_json::from_str(&run_json).map_err(|error| { SpacetimeClientError::Runtime(format!("big fish run run_json 非法: {error}")) })?; Ok(map_big_fish_runtime_snapshot(run)) } pub(crate) fn map_match3d_agent_session_procedure_result( result: Match3DAgentSessionProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::Procedure( result .error_message .unwrap_or_else(|| "SpacetimeDB procedure 返回未知错误".to_string()), )); } let session_json = result.session_json.ok_or_else(|| { SpacetimeClientError::Procedure( "SpacetimeDB procedure 未返回 match3d agent session 快照".to_string(), ) })?; let session = serde_json::from_str::(&session_json).map_err(|error| { SpacetimeClientError::Runtime(format!("match3d session_json 非法: {error}")) })?; Ok(map_match3d_agent_session_snapshot(session)) } pub(crate) fn map_match3d_work_procedure_result( result: Match3DWorkProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::Procedure( result .error_message .unwrap_or_else(|| "SpacetimeDB procedure 返回未知错误".to_string()), )); } let work_json = result.work_json.ok_or_else(|| { SpacetimeClientError::Procedure( "SpacetimeDB procedure 未返回 match3d work 快照".to_string(), ) })?; let work = serde_json::from_str::(&work_json).map_err(|error| { SpacetimeClientError::Runtime(format!("match3d work_json 非法: {error}")) })?; Ok(map_match3d_work_snapshot(work)) } pub(crate) fn map_match3d_works_procedure_result( result: Match3DWorksProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::Procedure( result .error_message .unwrap_or_else(|| "SpacetimeDB procedure 返回未知错误".to_string()), )); } let items_json = result.items_json.ok_or_else(|| { SpacetimeClientError::Procedure( "SpacetimeDB procedure 未返回 match3d works 快照".to_string(), ) })?; let items = serde_json::from_str::>(&items_json).map_err(|error| { SpacetimeClientError::Runtime(format!("match3d works items_json 非法: {error}")) })?; Ok(items.into_iter().map(map_match3d_work_snapshot).collect()) } pub(crate) fn map_match3d_run_procedure_result( result: Match3DRunProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::Procedure( result .error_message .unwrap_or_else(|| "SpacetimeDB procedure 返回未知错误".to_string()), )); } let run_json = result.run_json.ok_or_else(|| { SpacetimeClientError::Procedure("SpacetimeDB procedure 未返回 match3d run 快照".to_string()) })?; map_match3d_run_json(run_json) } pub(crate) fn map_match3d_click_item_procedure_result( result: Match3DClickItemProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::Procedure( result .error_message .unwrap_or_else(|| "SpacetimeDB procedure 返回未知错误".to_string()), )); } let run_json = result.run_json.ok_or_else(|| { SpacetimeClientError::Procedure( "SpacetimeDB procedure 未返回 match3d click run 快照".to_string(), ) })?; let run = map_match3d_run_json(run_json)?; let accepted = result.status == "Accepted"; let accepted_item_instance_id = result.accepted_item_instance_id.clone(); let entered_slot_index = accepted_item_instance_id.as_deref().and_then(|item_id| { run.items .iter() .find(|item| item.item_instance_id == item_id) .and_then(|item| item.tray_slot_index) }); Ok(Match3DClickConfirmationRecord { status: result.status.clone(), accepted, reject_reason: if accepted { None } else { Some(result.status) }, accepted_item_instance_id, entered_slot_index, cleared_item_instance_ids: result.cleared_item_instance_ids, failure_reason: result.failure_reason, run, }) } pub(crate) fn map_square_hole_agent_session_procedure_result( result: SquareHoleAgentSessionProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let session_json = result .session_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("square hole agent session 快照"))?; let session = serde_json::from_str::(&session_json).map_err( |error| SpacetimeClientError::Runtime(format!("square hole session_json 非法: {error}")), )?; Ok(map_square_hole_agent_session_snapshot(session)) } pub(crate) fn map_square_hole_work_procedure_result( result: SquareHoleWorkProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let work_json = result .work_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("square hole work 快照"))?; let work = serde_json::from_str::(&work_json).map_err(|error| { SpacetimeClientError::Runtime(format!("square hole work_json 非法: {error}")) })?; Ok(map_square_hole_work_snapshot(work)) } pub(crate) fn map_square_hole_works_procedure_result( result: SquareHoleWorksProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let items_json = result .items_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("square hole works 快照"))?; let items = serde_json::from_str::>(&items_json).map_err(|error| { SpacetimeClientError::Runtime(format!("square hole works items_json 非法: {error}")) })?; Ok(items .into_iter() .map(map_square_hole_work_snapshot) .collect()) } pub(crate) fn map_square_hole_run_procedure_result( result: SquareHoleRunProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let run_json = result .run_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("square hole run 快照"))?; map_square_hole_run_json(run_json) } pub(crate) fn map_square_hole_drop_shape_procedure_result( result: SquareHoleDropShapeProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let run_json = result .run_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("square hole drop run 快照"))?; let feedback_json = result .feedback_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("square hole drop feedback 快照"))?; let run = map_square_hole_run_json(run_json)?; let feedback = serde_json::from_str::(&feedback_json) .map_err(|error| { SpacetimeClientError::Runtime(format!("square hole feedback_json 非法: {error}")) })?; Ok(SquareHoleDropConfirmationRecord { status: result.status, accepted: feedback.accepted, reject_reason: feedback.reject_reason.clone(), failure_reason: result.failure_reason, feedback: map_square_hole_feedback_snapshot(feedback), run, }) } pub(crate) fn map_visual_novel_agent_session_procedure_result( result: VisualNovelAgentSessionProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let session_json = result .session_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("visual novel agent session 快照"))?; let session = serde_json::from_str::(&session_json) .map_err(|error| { SpacetimeClientError::Runtime(format!("visual novel session_json 非法: {error}")) })?; Ok(map_visual_novel_agent_session_snapshot(session)) } pub(crate) fn map_visual_novel_work_procedure_result( result: VisualNovelWorkProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let work_json = result .work_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("visual novel work 快照"))?; let work = serde_json::from_str::(&work_json).map_err(|error| { SpacetimeClientError::Runtime(format!("visual novel work_json 非法: {error}")) })?; Ok(map_visual_novel_work_snapshot(work)) } pub(crate) fn map_visual_novel_works_procedure_result( result: VisualNovelWorksProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let items_json = result .items_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("visual novel works 快照"))?; let items = serde_json::from_str::>(&items_json).map_err(|error| { SpacetimeClientError::Runtime(format!("visual novel works items_json 非法: {error}")) })?; Ok(items .into_iter() .map(map_visual_novel_work_snapshot) .collect()) } pub(crate) fn map_visual_novel_run_procedure_result( result: VisualNovelRunProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let run_json = result .run_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("visual novel run 快照"))?; let run = serde_json::from_str::(&run_json).map_err(|error| { SpacetimeClientError::Runtime(format!("visual novel run_json 非法: {error}")) })?; Ok(map_visual_novel_run_snapshot(run)) } pub(crate) fn map_visual_novel_history_procedure_result( result: VisualNovelHistoryProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let items_json = result .items_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("visual novel history 快照"))?; let items = serde_json::from_str::>(&items_json) .map_err(|error| { SpacetimeClientError::Runtime(format!("visual novel history items_json 非法: {error}")) })?; Ok(items .into_iter() .map(map_visual_novel_history_entry) .collect()) } pub(crate) fn map_visual_novel_runtime_event_procedure_result( result: VisualNovelRuntimeEventProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let event_json = result .event_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("visual novel runtime event 快照"))?; let event = serde_json::from_str::(&event_json).map_err( |error| SpacetimeClientError::Runtime(format!("visual novel event_json 非法: {error}")), )?; Ok(map_visual_novel_runtime_event(event)) } pub(crate) fn map_story_session_procedure_result( result: StorySessionProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let session = result .session .ok_or_else(|| SpacetimeClientError::missing_snapshot("story session 快照"))?; let event = result .event .ok_or_else(|| SpacetimeClientError::missing_snapshot("story event 快照"))?; Ok(StorySessionResultRecord { session: map_story_session_snapshot(session), event: map_story_event_snapshot(event), }) } pub(crate) fn map_story_session_state_procedure_result( result: StorySessionStateProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let session = result .session .ok_or_else(|| SpacetimeClientError::missing_snapshot("story session state 快照"))?; Ok(StorySessionStateRecord { session: map_story_session_snapshot(session), events: result .events .into_iter() .map(map_story_event_snapshot) .collect(), }) } pub(crate) fn map_runtime_inventory_state_procedure_result( result: RuntimeInventoryStateProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .snapshot .ok_or_else(|| SpacetimeClientError::missing_snapshot("runtime inventory state 快照"))?; Ok(build_runtime_inventory_state_record( map_runtime_inventory_state_snapshot(snapshot), )) } pub(crate) fn map_battle_state_procedure_result( result: BattleStateProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .snapshot .ok_or_else(|| SpacetimeClientError::missing_snapshot("battle_state 快照"))?; Ok(build_battle_state_record(map_battle_state_snapshot( snapshot, ))) } pub(crate) fn map_resolve_combat_action_procedure_result( result: ResolveCombatActionProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let action_result = result .result .ok_or_else(|| SpacetimeClientError::missing_snapshot("战斗结算结果"))?; Ok(build_resolve_combat_action_record( map_resolve_combat_action_result(action_result), )) } pub(crate) fn map_npc_battle_interaction_procedure_result( result: NpcBattleInteractionProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let interaction_result = result .result .ok_or_else(|| SpacetimeClientError::missing_snapshot("NPC 开战结果"))?; Ok(build_npc_battle_interaction_record( map_npc_battle_interaction_result(interaction_result), )) } pub(crate) fn map_entity_binding_snapshot( snapshot: AssetEntityBindingSnapshot, ) -> module_assets::AssetEntityBindingSnapshot { module_assets::AssetEntityBindingSnapshot { binding_id: snapshot.binding_id, asset_object_id: snapshot.asset_object_id, entity_kind: snapshot.entity_kind, entity_id: snapshot.entity_id, slot: snapshot.slot, asset_kind: snapshot.asset_kind, owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_snapshot( snapshot: AssetObjectUpsertSnapshot, ) -> module_assets::AssetObjectUpsertSnapshot { module_assets::AssetObjectUpsertSnapshot { asset_object_id: snapshot.asset_object_id, bucket: snapshot.bucket, object_key: snapshot.object_key, access_policy: map_access_policy_back(snapshot.access_policy), content_type: snapshot.content_type, content_length: snapshot.content_length, content_hash: snapshot.content_hash, version: snapshot.version, source_job_id: snapshot.source_job_id, owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, entity_id: snapshot.entity_id, asset_kind: snapshot.asset_kind, created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_asset_history_entry_snapshot( snapshot: AssetHistoryEntrySnapshot, ) -> module_assets::AssetHistoryEntrySnapshot { module_assets::AssetHistoryEntrySnapshot { asset_object_id: snapshot.asset_object_id, asset_kind: snapshot.asset_kind, image_src: snapshot.image_src, owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, entity_id: snapshot.entity_id, created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_setting_snapshot( snapshot: RuntimeSettingSnapshot, ) -> module_runtime::RuntimeSettingSnapshot { module_runtime::RuntimeSettingSnapshot { user_id: snapshot.user_id, music_volume: snapshot.music_volume, platform_theme: map_runtime_platform_theme_back(snapshot.platform_theme), created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_browse_history_snapshot( snapshot: RuntimeBrowseHistorySnapshot, ) -> module_runtime::RuntimeBrowseHistorySnapshot { module_runtime::RuntimeBrowseHistorySnapshot { browse_history_id: snapshot.browse_history_id, user_id: snapshot.user_id, owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, world_name: snapshot.world_name, subtitle: snapshot.subtitle, summary_text: snapshot.summary_text, cover_image_src: snapshot.cover_image_src, theme_mode: map_runtime_browse_history_theme_mode_back(snapshot.theme_mode), author_display_name: snapshot.author_display_name, visited_at_micros: snapshot.visited_at_micros, created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_profile_dashboard_snapshot( snapshot: RuntimeProfileDashboardSnapshot, ) -> module_runtime::RuntimeProfileDashboardSnapshot { module_runtime::RuntimeProfileDashboardSnapshot { user_id: snapshot.user_id, wallet_balance: snapshot.wallet_balance, total_play_time_ms: snapshot.total_play_time_ms, played_world_count: snapshot.played_world_count, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_analytics_bucket_metric( bucket: AnalyticsBucketMetric, ) -> module_runtime::AnalyticsBucketMetric { module_runtime::AnalyticsBucketMetric { bucket_key: bucket.bucket_key, bucket_start_date_key: bucket.bucket_start_date_key, bucket_end_date_key: bucket.bucket_end_date_key, value: bucket.value, } } pub(crate) fn map_runtime_profile_wallet_ledger_entry_snapshot( snapshot: RuntimeProfileWalletLedgerEntrySnapshot, ) -> module_runtime::RuntimeProfileWalletLedgerEntrySnapshot { module_runtime::RuntimeProfileWalletLedgerEntrySnapshot { wallet_ledger_id: snapshot.wallet_ledger_id, user_id: snapshot.user_id, amount_delta: snapshot.amount_delta, balance_after: snapshot.balance_after, source_type: map_runtime_profile_wallet_ledger_source_type_back(snapshot.source_type), created_at_micros: snapshot.created_at_micros, } } pub(crate) fn map_runtime_profile_recharge_center_snapshot( snapshot: RuntimeProfileRechargeCenterSnapshot, ) -> module_runtime::RuntimeProfileRechargeCenterSnapshot { module_runtime::RuntimeProfileRechargeCenterSnapshot { user_id: snapshot.user_id, wallet_balance: snapshot.wallet_balance, membership: map_runtime_profile_membership_snapshot(snapshot.membership), point_products: snapshot .point_products .into_iter() .map(map_runtime_profile_recharge_product_snapshot) .collect(), membership_products: snapshot .membership_products .into_iter() .map(map_runtime_profile_recharge_product_snapshot) .collect(), benefits: snapshot .benefits .into_iter() .map(map_runtime_profile_membership_benefit_snapshot) .collect(), latest_order: snapshot .latest_order .map(map_runtime_profile_recharge_order_snapshot), has_points_recharged: snapshot.has_points_recharged, } } pub(crate) fn map_runtime_profile_recharge_product_snapshot( snapshot: RuntimeProfileRechargeProductSnapshot, ) -> module_runtime::RuntimeProfileRechargeProductSnapshot { module_runtime::RuntimeProfileRechargeProductSnapshot { product_id: snapshot.product_id, title: snapshot.title, price_cents: snapshot.price_cents, kind: map_runtime_profile_recharge_product_kind_back(snapshot.kind), points_amount: snapshot.points_amount, bonus_points: snapshot.bonus_points, duration_days: snapshot.duration_days, badge_label: snapshot.badge_label, description: snapshot.description, tier: map_runtime_profile_membership_tier_back(snapshot.tier), } } pub(crate) fn map_runtime_profile_recharge_product_config_snapshot( snapshot: RuntimeProfileRechargeProductConfigSnapshot, ) -> module_runtime::RuntimeProfileRechargeProductConfigSnapshot { module_runtime::RuntimeProfileRechargeProductConfigSnapshot { product_id: snapshot.product_id, title: snapshot.title, price_cents: snapshot.price_cents, kind: map_runtime_profile_recharge_product_kind_back(snapshot.kind), points_amount: snapshot.points_amount, bonus_points: snapshot.bonus_points, duration_days: snapshot.duration_days, badge_label: snapshot.badge_label, description: snapshot.description, tier: map_runtime_profile_membership_tier_back(snapshot.tier), enabled: snapshot.enabled, sort_order: snapshot.sort_order, created_by: snapshot.created_by, created_at_micros: snapshot.created_at_micros, updated_by: snapshot.updated_by, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_profile_membership_benefit_snapshot( snapshot: RuntimeProfileMembershipBenefitSnapshot, ) -> module_runtime::RuntimeProfileMembershipBenefitSnapshot { module_runtime::RuntimeProfileMembershipBenefitSnapshot { benefit_name: snapshot.benefit_name, normal_value: snapshot.normal_value, month_value: snapshot.month_value, season_value: snapshot.season_value, year_value: snapshot.year_value, } } pub(crate) fn map_runtime_profile_membership_snapshot( snapshot: RuntimeProfileMembershipSnapshot, ) -> module_runtime::RuntimeProfileMembershipSnapshot { module_runtime::RuntimeProfileMembershipSnapshot { user_id: snapshot.user_id, status: map_runtime_profile_membership_status_back(snapshot.status), tier: map_runtime_profile_membership_tier_back(snapshot.tier), started_at_micros: snapshot.started_at_micros, expires_at_micros: snapshot.expires_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_profile_recharge_order_snapshot( snapshot: RuntimeProfileRechargeOrderSnapshot, ) -> module_runtime::RuntimeProfileRechargeOrderSnapshot { module_runtime::RuntimeProfileRechargeOrderSnapshot { order_id: snapshot.order_id, user_id: snapshot.user_id, product_id: snapshot.product_id, product_title: snapshot.product_title, kind: map_runtime_profile_recharge_product_kind_back(snapshot.kind), amount_cents: snapshot.amount_cents, status: map_runtime_profile_recharge_order_status_back(snapshot.status), payment_channel: snapshot.payment_channel, paid_at_micros: snapshot.paid_at_micros, provider_transaction_id: snapshot.provider_transaction_id, created_at_micros: snapshot.created_at_micros, points_delta: snapshot.points_delta, membership_expires_at_micros: snapshot.membership_expires_at_micros, } } pub(crate) fn map_runtime_profile_feedback_submission_snapshot( snapshot: RuntimeProfileFeedbackSubmissionSnapshot, ) -> module_runtime::RuntimeProfileFeedbackSubmissionSnapshot { module_runtime::RuntimeProfileFeedbackSubmissionSnapshot { feedback_id: snapshot.feedback_id, user_id: snapshot.user_id, description: snapshot.description, contact_phone: snapshot.contact_phone, evidence_json: snapshot.evidence_json, status: map_runtime_profile_feedback_status_back(snapshot.status), created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_referral_invite_center_snapshot( snapshot: RuntimeReferralInviteCenterSnapshot, ) -> module_runtime::RuntimeReferralInviteCenterSnapshot { module_runtime::RuntimeReferralInviteCenterSnapshot { user_id: snapshot.user_id, invite_code: snapshot.invite_code, invite_link_path: snapshot.invite_link_path, invited_count: snapshot.invited_count, rewarded_invite_count: snapshot.rewarded_invite_count, today_inviter_reward_count: snapshot.today_inviter_reward_count, today_inviter_reward_remaining: snapshot.today_inviter_reward_remaining, reward_points: snapshot.reward_points, invited_users: snapshot .invited_users .into_iter() .map(|user| module_runtime::RuntimeReferralInvitedUserSnapshot { user_id: user.user_id, display_name: user.display_name, avatar_url: user.avatar_url, bound_at_micros: user.bound_at_micros, }) .collect(), has_redeemed_code: snapshot.has_redeemed_code, bound_inviter_user_id: snapshot.bound_inviter_user_id, bound_at_micros: snapshot.bound_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_referral_redeem_snapshot( snapshot: RuntimeReferralRedeemSnapshot, ) -> module_runtime::RuntimeReferralRedeemSnapshot { module_runtime::RuntimeReferralRedeemSnapshot { center: map_runtime_referral_invite_center_snapshot(snapshot.center), invitee_reward_granted: snapshot.invitee_reward_granted, inviter_reward_granted: snapshot.inviter_reward_granted, invitee_balance_after: snapshot.invitee_balance_after, inviter_balance_after: snapshot.inviter_balance_after, } } pub(crate) fn map_runtime_profile_reward_code_redeem_snapshot( snapshot: RuntimeProfileRewardCodeRedeemSnapshot, ) -> module_runtime::RuntimeProfileRewardCodeRedeemSnapshot { module_runtime::RuntimeProfileRewardCodeRedeemSnapshot { wallet_balance: snapshot.wallet_balance, amount_granted: snapshot.amount_granted, ledger_entry: map_runtime_profile_wallet_ledger_entry_snapshot(snapshot.ledger_entry), } } pub(crate) fn map_runtime_profile_task_config_snapshot( snapshot: RuntimeProfileTaskConfigSnapshot, ) -> module_runtime::RuntimeProfileTaskConfigSnapshot { module_runtime::RuntimeProfileTaskConfigSnapshot { task_id: snapshot.task_id, title: snapshot.title, description: snapshot.description, event_key: snapshot.event_key, cycle: map_runtime_profile_task_cycle_back(snapshot.cycle), scope_kind: map_runtime_tracking_scope_kind_back(snapshot.scope_kind), threshold: snapshot.threshold, reward_points: snapshot.reward_points, enabled: snapshot.enabled, sort_order: snapshot.sort_order, created_by: snapshot.created_by, created_at_micros: snapshot.created_at_micros, updated_by: snapshot.updated_by, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_profile_task_item_snapshot( snapshot: RuntimeProfileTaskItemSnapshot, ) -> module_runtime::RuntimeProfileTaskItemSnapshot { module_runtime::RuntimeProfileTaskItemSnapshot { task_id: snapshot.task_id, title: snapshot.title, description: snapshot.description, event_key: snapshot.event_key, cycle: map_runtime_profile_task_cycle_back(snapshot.cycle), threshold: snapshot.threshold, progress_count: snapshot.progress_count, reward_points: snapshot.reward_points, status: map_runtime_profile_task_status_back(snapshot.status), day_key: snapshot.day_key, claimed_at_micros: snapshot.claimed_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_profile_task_center_snapshot( snapshot: RuntimeProfileTaskCenterSnapshot, ) -> module_runtime::RuntimeProfileTaskCenterSnapshot { module_runtime::RuntimeProfileTaskCenterSnapshot { user_id: snapshot.user_id, day_key: snapshot.day_key, wallet_balance: snapshot.wallet_balance, tasks: snapshot .tasks .into_iter() .map(map_runtime_profile_task_item_snapshot) .collect(), updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_profile_task_claim_snapshot( snapshot: RuntimeProfileTaskClaimSnapshot, ) -> module_runtime::RuntimeProfileTaskClaimSnapshot { module_runtime::RuntimeProfileTaskClaimSnapshot { user_id: snapshot.user_id, task_id: snapshot.task_id, day_key: snapshot.day_key, reward_points: snapshot.reward_points, wallet_balance: snapshot.wallet_balance, ledger_entry: map_runtime_profile_wallet_ledger_entry_snapshot(snapshot.ledger_entry), center: map_runtime_profile_task_center_snapshot(snapshot.center), } } pub(crate) fn map_runtime_profile_redeem_code_snapshot( snapshot: RuntimeProfileRedeemCodeSnapshot, ) -> module_runtime::RuntimeProfileRedeemCodeSnapshot { module_runtime::RuntimeProfileRedeemCodeSnapshot { code: snapshot.code, mode: map_runtime_profile_redeem_code_mode_back(snapshot.mode), reward_points: snapshot.reward_points, max_uses: snapshot.max_uses, global_used_count: snapshot.global_used_count, enabled: snapshot.enabled, allowed_user_ids: snapshot.allowed_user_ids, created_by: snapshot.created_by, created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_profile_invite_code_snapshot( snapshot: RuntimeProfileInviteCodeSnapshot, ) -> module_runtime::RuntimeProfileInviteCodeSnapshot { module_runtime::RuntimeProfileInviteCodeSnapshot { user_id: snapshot.user_id, invite_code: snapshot.invite_code, metadata_json: snapshot.metadata_json, starts_at_micros: snapshot.starts_at_micros, expires_at_micros: snapshot.expires_at_micros, created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_profile_played_world_snapshot( snapshot: RuntimeProfilePlayedWorldSnapshot, ) -> module_runtime::RuntimeProfilePlayedWorldSnapshot { module_runtime::RuntimeProfilePlayedWorldSnapshot { played_world_id: snapshot.played_world_id, user_id: snapshot.user_id, world_key: snapshot.world_key, owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, world_type: snapshot.world_type, world_title: snapshot.world_title, world_subtitle: snapshot.world_subtitle, first_played_at_micros: snapshot.first_played_at_micros, last_played_at_micros: snapshot.last_played_at_micros, last_observed_play_time_ms: snapshot.last_observed_play_time_ms, } } pub(crate) fn map_runtime_profile_play_stats_snapshot( snapshot: RuntimeProfilePlayStatsSnapshot, ) -> module_runtime::RuntimeProfilePlayStatsSnapshot { module_runtime::RuntimeProfilePlayStatsSnapshot { user_id: snapshot.user_id, total_play_time_ms: snapshot.total_play_time_ms, played_works: snapshot .played_works .into_iter() .map(map_runtime_profile_played_world_snapshot) .collect(), updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_snapshot_snapshot( snapshot: RuntimeSnapshot, ) -> module_runtime::RuntimeSnapshot { module_runtime::RuntimeSnapshot { user_id: snapshot.user_id, version: snapshot.version, saved_at_micros: snapshot.saved_at_micros, bottom_tab: snapshot.bottom_tab, game_state_json: snapshot.game_state_json, current_story_json: snapshot.current_story_json, created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_profile_save_archive_snapshot( snapshot: RuntimeProfileSaveArchiveSnapshot, ) -> module_runtime::RuntimeProfileSaveArchiveSnapshot { module_runtime::RuntimeProfileSaveArchiveSnapshot { archive_id: snapshot.archive_id, user_id: snapshot.user_id, world_key: snapshot.world_key, owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, world_type: snapshot.world_type, world_name: snapshot.world_name, subtitle: snapshot.subtitle, summary_text: snapshot.summary_text, cover_image_src: snapshot.cover_image_src, saved_at_micros: snapshot.saved_at_micros, bottom_tab: snapshot.bottom_tab, game_state_json: snapshot.game_state_json, current_story_json: snapshot.current_story_json, created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_custom_world_library_entry_from_profile_snapshot( snapshot: CustomWorldProfileSnapshot, ) -> Result { let profile = serde_json::from_str::(&snapshot.profile_payload_json) .map_err(|error| { SpacetimeClientError::Runtime(format!( "custom world profile payload JSON 非法: {error}" )) })?; Ok(CustomWorldLibraryEntryRecord { owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, public_work_code: snapshot.public_work_code, author_public_user_code: snapshot.author_public_user_code, profile, visibility: map_custom_world_publication_status(snapshot.publication_status).to_string(), published_at: snapshot.published_at_micros.map(format_timestamp_micros), updated_at: format_timestamp_micros(snapshot.updated_at_micros), author_display_name: snapshot.author_display_name, world_name: snapshot.world_name, subtitle: snapshot.subtitle, summary_text: snapshot.summary_text, cover_image_src: snapshot.cover_image_src, theme_mode: format_custom_world_theme_mode(map_custom_world_theme_mode_back( snapshot.theme_mode, )) .to_string(), playable_npc_count: snapshot.playable_npc_count, landmark_count: snapshot.landmark_count, play_count: snapshot.play_count, remix_count: snapshot.remix_count, like_count: snapshot.like_count, recent_play_count_7d: 0, }) } pub(crate) fn map_custom_world_gallery_entry_snapshot( snapshot: CustomWorldGalleryEntrySnapshot, ) -> Result { Ok(CustomWorldGalleryEntryRecord { owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, public_work_code: snapshot.public_work_code, author_public_user_code: snapshot.author_public_user_code, visibility: "published".to_string(), published_at: Some(format_timestamp_micros(snapshot.published_at_micros)), updated_at: format_timestamp_micros(snapshot.updated_at_micros), author_display_name: snapshot.author_display_name, world_name: snapshot.world_name, subtitle: snapshot.subtitle, summary_text: snapshot.summary_text, cover_image_src: snapshot.cover_image_src, theme_mode: format_custom_world_theme_mode(map_custom_world_theme_mode_back( snapshot.theme_mode, )) .to_string(), playable_npc_count: snapshot.playable_npc_count, landmark_count: snapshot.landmark_count, play_count: snapshot.play_count, remix_count: snapshot.remix_count, like_count: snapshot.like_count, recent_play_count_7d: snapshot.recent_play_count_7_d, }) } pub(crate) fn map_custom_world_published_profile_compile_snapshot( snapshot: CustomWorldPublishedProfileCompileSnapshot, ) -> Result { let compiled_profile = serde_json::from_str::(&snapshot.compiled_profile_payload_json) .map_err(|error| { SpacetimeClientError::Runtime(format!( "published profile compile JSON 非法: {error}" )) })?; Ok(CustomWorldPublishedProfileCompileRecord { profile_id: snapshot.profile_id, owner_user_id: snapshot.owner_user_id, world_name: snapshot.world_name, subtitle: snapshot.subtitle, summary_text: snapshot.summary_text, theme_mode: format_custom_world_theme_mode(map_custom_world_theme_mode_back( snapshot.theme_mode, )) .to_string(), cover_image_src: snapshot.cover_image_src, playable_npc_count: snapshot.playable_npc_count, landmark_count: snapshot.landmark_count, author_display_name: snapshot.author_display_name, compiled_profile: compiled_profile, updated_at: format_timestamp_micros(snapshot.updated_at_micros), }) } pub(crate) fn map_custom_world_work_summary_snapshot( snapshot: CustomWorldWorkSummarySnapshot, ) -> Result { Ok(CustomWorldWorkSummaryRecord { work_id: snapshot.work_id, source_type: snapshot.source_type, status: snapshot.status, title: snapshot.title, subtitle: snapshot.subtitle, summary: snapshot.summary, cover_image_src: snapshot.cover_image_src, cover_render_mode: snapshot.cover_render_mode, cover_character_image_srcs: parse_json_string_array( &snapshot.cover_character_image_srcs_json, "custom world work cover_character_image_srcs_json", )?, updated_at: format_timestamp_micros(snapshot.updated_at_micros), published_at: snapshot.published_at_micros.map(format_timestamp_micros), stage: snapshot.stage.map(map_rpg_agent_stage), stage_label: snapshot.stage_label, playable_npc_count: snapshot.playable_npc_count, landmark_count: snapshot.landmark_count, role_visual_ready_count: snapshot.role_visual_ready_count, role_animation_ready_count: snapshot.role_animation_ready_count, role_asset_summary_label: snapshot.role_asset_summary_label, session_id: snapshot.session_id, profile_id: snapshot.profile_id, can_resume: snapshot.can_resume, can_enter_world: snapshot.can_enter_world, blocker_count: snapshot.blocker_count, publish_ready: snapshot.publish_ready, }) } pub(crate) fn map_custom_world_agent_session_snapshot( snapshot: CustomWorldAgentSessionSnapshot, ) -> Result { let anchor_content = parse_json_value( &snapshot.anchor_content_json, "custom world agent anchor_content_json", )?; let creator_intent = parse_optional_json_value( snapshot.creator_intent_json.as_deref(), serde_json::json!({}), "custom world agent creator_intent_json", )?; let creator_intent_readiness = parse_json_value( &snapshot.creator_intent_readiness_json, "custom world agent creator_intent_readiness_json", )?; let anchor_pack = parse_optional_json_value( snapshot.anchor_pack_json.as_deref(), serde_json::json!({}), "custom world agent anchor_pack_json", )?; let lock_state = parse_optional_json_value( snapshot.lock_state_json.as_deref(), serde_json::json!({}), "custom world agent lock_state_json", )?; let draft_profile = parse_optional_json_value( snapshot.draft_profile_json.as_deref(), serde_json::json!({}), "custom world agent draft_profile_json", )?; let pending_clarifications = parse_json_array( &snapshot.pending_clarifications_json, "custom world agent pending_clarifications_json", )?; let suggested_actions = parse_json_array( &snapshot.suggested_actions_json, "custom world agent suggested_actions_json", )?; let recommended_replies = parse_json_string_array( &snapshot.recommended_replies_json, "custom world agent recommended_replies_json", )?; let quality_findings = parse_json_array( &snapshot.quality_findings_json, "custom world agent quality_findings_json", )?; let asset_coverage = parse_json_value( &snapshot.asset_coverage_json, "custom world agent asset_coverage_json", )?; let checkpoints_json = parse_json_array( &snapshot.checkpoints_json, "custom world agent checkpoints_json", )?; let checkpoints = checkpoints_json .into_iter() .map(map_custom_world_checkpoint_record) .collect::, _>>()?; let supported_actions = parse_supported_actions_json(&snapshot.supported_actions_json)?; let publish_gate = snapshot .publish_gate_json .as_deref() .map(parse_custom_world_publish_gate_record) .transpose()?; Ok(CustomWorldAgentSessionRecord { session_id: snapshot.session_id, seed_text: snapshot.seed_text, current_turn: snapshot.current_turn, anchor_content, progress_percent: snapshot.progress_percent, last_assistant_reply: snapshot.last_assistant_reply, stage: map_rpg_agent_stage(snapshot.stage), focus_card_id: snapshot.focus_card_id, creator_intent, creator_intent_readiness, anchor_pack, lock_state, draft_profile, messages: snapshot .messages .into_iter() .map(map_custom_world_agent_message_snapshot) .collect(), draft_cards: snapshot .draft_cards .into_iter() .map(map_custom_world_draft_card_snapshot) .collect::, _>>()?, pending_clarifications, suggested_actions, recommended_replies, quality_findings, asset_coverage, checkpoints, supported_actions, publish_gate, result_preview: snapshot .result_preview_json .as_deref() .map(|value| parse_json_value(value, "custom world agent result_preview_json")) .transpose()?, updated_at: format_timestamp_micros(snapshot.updated_at_micros), }) } pub(crate) fn map_custom_world_agent_message_snapshot( snapshot: CustomWorldAgentMessageSnapshot, ) -> CustomWorldAgentMessageRecord { CustomWorldAgentMessageRecord { message_id: snapshot.message_id, role: format_rpg_agent_message_role(snapshot.role).to_string(), kind: format_rpg_agent_message_kind(snapshot.kind).to_string(), text: snapshot.text, created_at: format_timestamp_micros(snapshot.created_at_micros), related_operation_id: snapshot.related_operation_id, } } pub(crate) fn map_custom_world_agent_operation_snapshot( snapshot: CustomWorldAgentOperationSnapshot, ) -> CustomWorldAgentOperationRecord { CustomWorldAgentOperationRecord { operation_id: snapshot.operation_id, operation_type: format_rpg_agent_operation_type(snapshot.operation_type).to_string(), status: format_rpg_agent_operation_status(snapshot.status).to_string(), phase_label: snapshot.phase_label, phase_detail: snapshot.phase_detail, progress: snapshot.progress, error_message: snapshot.error_message, started_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_custom_world_draft_card_snapshot( snapshot: CustomWorldDraftCardSnapshot, ) -> Result { Ok(CustomWorldDraftCardRecord { card_id: snapshot.card_id, kind: format_rpg_agent_draft_card_kind(snapshot.kind).to_string(), title: snapshot.title, subtitle: snapshot.subtitle, summary: snapshot.summary, status: format_rpg_agent_draft_card_status(snapshot.status).to_string(), linked_ids: parse_json_string_array( &snapshot.linked_ids_json, "custom world draft_card linked_ids_json", )?, warning_count: snapshot.warning_count, asset_status: snapshot .asset_status .map(format_custom_world_role_asset_status_back), asset_status_label: snapshot.asset_status_label, detail_payload: snapshot .detail_payload_json .as_deref() .map(|value| parse_json_value(value, "custom world draft_card detail_payload_json")) .transpose()?, }) } pub(crate) fn map_custom_world_draft_card_detail_snapshot( snapshot: CustomWorldDraftCardDetailSnapshot, ) -> Result { Ok(CustomWorldDraftCardDetailRecord { card_id: snapshot.card_id, kind: format_rpg_agent_draft_card_kind(snapshot.kind).to_string(), title: snapshot.title, sections: snapshot .sections .into_iter() .map(map_custom_world_draft_card_detail_section_snapshot) .collect(), linked_ids: parse_json_string_array( &snapshot.linked_ids_json, "custom world card detail linked_ids_json", )?, locked: snapshot.locked, editable: snapshot.editable, editable_section_ids: parse_json_string_array( &snapshot.editable_section_ids_json, "custom world card detail editable_section_ids_json", )?, warning_messages: parse_json_string_array( &snapshot.warning_messages_json, "custom world card detail warning_messages_json", )?, asset_status: snapshot .asset_status .map(format_custom_world_role_asset_status_back), asset_status_label: snapshot.asset_status_label, }) } pub(crate) fn map_custom_world_draft_card_detail_section_snapshot( snapshot: CustomWorldDraftCardDetailSectionSnapshot, ) -> CustomWorldDraftCardDetailSectionRecord { CustomWorldDraftCardDetailSectionRecord { section_id: snapshot.section_id, label: snapshot.label, value: snapshot.value, } } pub(crate) fn map_big_fish_session_snapshot( snapshot: BigFishSessionSnapshot, ) -> BigFishSessionRecord { BigFishSessionRecord { session_id: snapshot.session_id, current_turn: snapshot.current_turn, progress_percent: snapshot.progress_percent, stage: format_big_fish_creation_stage(snapshot.stage).to_string(), anchor_pack: map_big_fish_anchor_pack(snapshot.anchor_pack), draft: snapshot.draft.map(map_big_fish_game_draft), asset_slots: snapshot .asset_slots .into_iter() .map(map_big_fish_asset_slot_snapshot) .collect(), asset_coverage: map_big_fish_asset_coverage(snapshot.asset_coverage), messages: snapshot .messages .into_iter() .map(map_big_fish_agent_message_snapshot) .collect(), last_assistant_reply: snapshot.last_assistant_reply, publish_ready: snapshot.publish_ready, updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } pub(crate) fn map_puzzle_agent_session_snapshot( snapshot: DomainPuzzleAgentSessionSnapshot, ) -> PuzzleAgentSessionRecord { PuzzleAgentSessionRecord { session_id: snapshot.session_id, seed_text: snapshot.seed_text, current_turn: snapshot.current_turn, progress_percent: snapshot.progress_percent, stage: snapshot.stage.as_str().to_string(), anchor_pack: map_puzzle_anchor_pack(snapshot.anchor_pack), draft: snapshot.draft.map(map_puzzle_result_draft), messages: snapshot .messages .into_iter() .map(map_puzzle_agent_message_snapshot) .collect(), last_assistant_reply: snapshot.last_assistant_reply, published_profile_id: snapshot.published_profile_id, suggested_actions: snapshot .suggested_actions .into_iter() .map(map_puzzle_suggested_action) .collect(), result_preview: snapshot.result_preview.map(map_puzzle_result_preview), updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } pub(crate) fn map_puzzle_anchor_pack(snapshot: DomainPuzzleAnchorPack) -> PuzzleAnchorPackRecord { PuzzleAnchorPackRecord { theme_promise: map_puzzle_anchor_item(snapshot.theme_promise), visual_subject: map_puzzle_anchor_item(snapshot.visual_subject), visual_mood: map_puzzle_anchor_item(snapshot.visual_mood), composition_hooks: map_puzzle_anchor_item(snapshot.composition_hooks), tags_and_forbidden: map_puzzle_anchor_item(snapshot.tags_and_forbidden), } } pub(crate) fn map_puzzle_anchor_item(snapshot: DomainPuzzleAnchorItem) -> PuzzleAnchorItemRecord { PuzzleAnchorItemRecord { key: snapshot.key, label: snapshot.label, value: snapshot.value, status: snapshot.status.as_str().to_string(), } } pub(crate) fn map_puzzle_result_draft( snapshot: DomainPuzzleResultDraft, ) -> PuzzleResultDraftRecord { PuzzleResultDraftRecord { work_title: snapshot.work_title, work_description: snapshot.work_description, level_name: snapshot.level_name, summary: snapshot.summary, theme_tags: snapshot.theme_tags, forbidden_directives: snapshot.forbidden_directives, creator_intent: snapshot.creator_intent.map(map_puzzle_creator_intent), anchor_pack: map_puzzle_anchor_pack(snapshot.anchor_pack), candidates: snapshot .candidates .into_iter() .map(map_puzzle_generated_image_candidate) .collect(), selected_candidate_id: snapshot.selected_candidate_id, cover_image_src: snapshot.cover_image_src, cover_asset_id: snapshot.cover_asset_id, generation_status: snapshot.generation_status, levels: snapshot .levels .into_iter() .map(map_puzzle_draft_level) .collect(), form_draft: snapshot.form_draft.map(map_puzzle_form_draft), } } pub(crate) fn map_puzzle_form_draft( snapshot: module_puzzle::PuzzleFormDraft, ) -> PuzzleFormDraftRecord { PuzzleFormDraftRecord { work_title: snapshot.work_title, work_description: snapshot.work_description, picture_description: snapshot.picture_description, } } pub(crate) fn map_puzzle_draft_level(snapshot: DomainPuzzleDraftLevel) -> PuzzleDraftLevelRecord { PuzzleDraftLevelRecord { level_id: snapshot.level_id, level_name: snapshot.level_name, picture_description: snapshot.picture_description, picture_reference: snapshot.picture_reference, ui_background_prompt: snapshot.ui_background_prompt, ui_background_image_src: snapshot.ui_background_image_src, ui_background_image_object_key: snapshot.ui_background_image_object_key, background_music: snapshot.background_music.map(map_puzzle_audio_asset), candidates: snapshot .candidates .into_iter() .map(map_puzzle_generated_image_candidate) .collect(), selected_candidate_id: snapshot.selected_candidate_id, cover_image_src: snapshot.cover_image_src, cover_asset_id: snapshot.cover_asset_id, generation_status: snapshot.generation_status, } } pub(crate) fn map_puzzle_audio_asset( asset: module_puzzle::PuzzleAudioAsset, ) -> PuzzleAudioAssetRecord { PuzzleAudioAssetRecord { task_id: asset.task_id, provider: asset.provider, asset_object_id: asset.asset_object_id, asset_kind: asset.asset_kind, audio_src: asset.audio_src, prompt: asset.prompt, title: asset.title, updated_at: asset.updated_at, } } pub(crate) fn map_puzzle_creator_intent( snapshot: DomainPuzzleCreatorIntent, ) -> PuzzleCreatorIntentRecord { PuzzleCreatorIntentRecord { source_mode: snapshot.source_mode, raw_messages_summary: snapshot.raw_messages_summary, theme_promise: snapshot.theme_promise, visual_subject: snapshot.visual_subject, visual_mood: snapshot.visual_mood, composition_hooks: snapshot.composition_hooks, theme_tags: snapshot.theme_tags, forbidden_directives: snapshot.forbidden_directives, } } pub(crate) fn map_puzzle_generated_image_candidate( snapshot: DomainPuzzleGeneratedImageCandidate, ) -> PuzzleGeneratedImageCandidateRecord { PuzzleGeneratedImageCandidateRecord { candidate_id: snapshot.candidate_id, image_src: snapshot.image_src, asset_id: snapshot.asset_id, prompt: snapshot.prompt, actual_prompt: snapshot.actual_prompt, source_type: snapshot.source_type, selected: snapshot.selected, } } pub(crate) fn map_puzzle_agent_message_snapshot( snapshot: DomainPuzzleAgentMessageSnapshot, ) -> PuzzleAgentMessageRecord { PuzzleAgentMessageRecord { message_id: snapshot.message_id, role: snapshot.role.as_str().to_string(), kind: snapshot.kind.as_str().to_string(), text: snapshot.text, created_at: format_timestamp_micros(snapshot.created_at_micros), } } fn map_match3d_agent_session_snapshot( snapshot: Match3DAgentSessionJsonRecord, ) -> Match3DAgentSessionRecord { let config = map_match3d_creator_config(snapshot.config); Match3DAgentSessionRecord { session_id: snapshot.session_id, current_turn: snapshot.current_turn, progress_percent: snapshot.progress_percent, stage: normalize_match3d_stage(&snapshot.stage).to_string(), anchor_pack: build_match3d_anchor_pack(&config), draft: snapshot .draft .map(|draft| map_match3d_result_draft(draft, config.reference_image_src.clone())), config: Some(config), messages: snapshot .messages .into_iter() .map(map_match3d_agent_message_snapshot) .collect(), last_assistant_reply: empty_string_to_none(snapshot.last_assistant_reply), published_profile_id: snapshot.published_profile_id, updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } fn map_match3d_creator_config( snapshot: Match3DCreatorConfigJsonRecord, ) -> Match3DCreatorConfigRecord { Match3DCreatorConfigRecord { theme_text: snapshot.theme_text, reference_image_src: snapshot.reference_image_src, clear_count: snapshot.clear_count, difficulty: snapshot.difficulty, asset_style_id: snapshot.asset_style_id, asset_style_label: snapshot.asset_style_label, asset_style_prompt: snapshot.asset_style_prompt, generate_click_sound: snapshot.generate_click_sound, } } fn map_match3d_result_draft( snapshot: Match3DDraftJsonRecord, reference_image_src: Option, ) -> Match3DResultDraftRecord { Match3DResultDraftRecord { profile_id: snapshot.profile_id, game_name: snapshot.game_name, theme_text: snapshot.theme_text, summary_text: snapshot.summary_text, tags: snapshot.tags, cover_image_src: None, reference_image_src, clear_count: snapshot.clear_count, difficulty: snapshot.difficulty, total_item_count: snapshot.clear_count.saturating_mul(3), publish_ready: false, blockers: Vec::new(), } } fn map_match3d_agent_message_snapshot( snapshot: Match3DAgentMessageJsonRecord, ) -> Match3DAgentMessageRecord { Match3DAgentMessageRecord { message_id: snapshot.message_id, role: snapshot.role, kind: normalize_match3d_message_kind(&snapshot.kind).to_string(), text: snapshot.text, created_at: format_timestamp_micros(snapshot.created_at_micros), } } fn map_match3d_work_snapshot(snapshot: Match3DWorkJsonRecord) -> Match3DWorkProfileRecord { let config = map_match3d_creator_config(snapshot.config); Match3DWorkProfileRecord { work_id: snapshot.profile_id.clone(), profile_id: snapshot.profile_id, owner_user_id: snapshot.owner_user_id, source_session_id: empty_string_to_none(snapshot.source_session_id), author_display_name: snapshot.author_display_name, game_name: snapshot.game_name, theme_text: snapshot.theme_text, summary: snapshot.summary_text, tags: snapshot.tags, cover_image_src: empty_string_to_none(snapshot.cover_image_src), cover_asset_id: empty_string_to_none(snapshot.cover_asset_id), reference_image_src: config.reference_image_src, clear_count: snapshot.clear_count, difficulty: snapshot.difficulty, publication_status: normalize_match3d_publication_status(&snapshot.publication_status) .to_string(), play_count: snapshot.play_count, updated_at: format_timestamp_micros(snapshot.updated_at_micros), published_at: snapshot.published_at_micros.map(format_timestamp_micros), publish_ready: snapshot.publish_ready, generated_item_assets_json: snapshot.generated_item_assets_json, } } fn map_match3d_run_json(run_json: String) -> Result { let run = serde_json::from_str::(&run_json).map_err(|error| { SpacetimeClientError::Runtime(format!("match3d run_json 非法: {error}")) })?; Ok(map_match3d_run_snapshot(run)) } fn map_match3d_run_snapshot(snapshot: Match3DRunJsonRecord) -> Match3DRunRecord { let tray_slots = snapshot .tray_slots .into_iter() .map(map_match3d_tray_slot_snapshot) .collect::>(); let items = snapshot .items .into_iter() .map(|item| { let tray_slot_index = tray_slots .iter() .find(|slot| { slot.item_instance_id.as_deref() == Some(item.item_instance_id.as_str()) }) .map(|slot| slot.slot_index); map_match3d_item_snapshot(item, tray_slot_index) }) .collect(); Match3DRunRecord { run_id: snapshot.run_id, profile_id: snapshot.profile_id, owner_user_id: String::new(), status: snapshot.status, snapshot_version: u64::from(snapshot.snapshot_version), started_at_ms: i64_to_u64_ms(snapshot.started_at_ms), duration_limit_ms: i64_to_u64_ms(snapshot.duration_limit_ms), server_now_ms: Some(i64_to_u64_ms(snapshot.server_now_ms)), remaining_ms: i64_to_u64_ms(snapshot.remaining_ms), clear_count: snapshot.clear_count, total_item_count: snapshot.total_item_count, cleared_item_count: snapshot.cleared_item_count, items, tray_slots, failure_reason: snapshot.failure_reason, last_confirmed_action_id: None, } } fn map_match3d_item_snapshot( snapshot: Match3DItemJsonRecord, tray_slot_index: Option, ) -> Match3DItemSnapshotRecord { Match3DItemSnapshotRecord { item_instance_id: snapshot.item_instance_id, item_type_id: snapshot.item_type_id, visual_key: snapshot.visual_key, x: snapshot.x, y: snapshot.y, radius: snapshot.radius, layer: snapshot.layer, state: snapshot.state, clickable: snapshot.clickable, tray_slot_index, } } fn map_match3d_tray_slot_snapshot(snapshot: Match3DTraySlotJsonRecord) -> Match3DTraySlotRecord { Match3DTraySlotRecord { slot_index: snapshot.slot_index, item_instance_id: snapshot.item_instance_id, item_type_id: snapshot.item_type_id, visual_key: snapshot.visual_key, } } fn build_match3d_anchor_pack(config: &Match3DCreatorConfigRecord) -> Match3DAnchorPackRecord { let clear_count = config.clear_count.to_string(); let difficulty = config.difficulty.to_string(); Match3DAnchorPackRecord { theme: build_match3d_anchor_item("theme", "题材主题", config.theme_text.as_str()), clear_count: build_match3d_anchor_item("clearCount", "需要消除次数", clear_count.as_str()), difficulty: build_match3d_anchor_item("difficulty", "难度", difficulty.as_str()), } } fn build_match3d_anchor_item(key: &str, label: &str, value: &str) -> Match3DAnchorItemRecord { Match3DAnchorItemRecord { key: key.to_string(), label: label.to_string(), value: value.to_string(), status: if value.trim().is_empty() { "missing" } else { "confirmed" } .to_string(), } } fn map_square_hole_agent_session_snapshot( snapshot: SquareHoleAgentSessionJsonRecord, ) -> SquareHoleAgentSessionRecord { let config = map_square_hole_creator_config(snapshot.config); SquareHoleAgentSessionRecord { session_id: snapshot.session_id, current_turn: snapshot.current_turn, progress_percent: snapshot.progress_percent, stage: normalize_square_hole_stage(&snapshot.stage).to_string(), anchor_pack: build_square_hole_anchor_pack(&config), config, draft: snapshot.draft.map(map_square_hole_result_draft), messages: snapshot .messages .into_iter() .map(map_square_hole_agent_message_snapshot) .collect(), last_assistant_reply: empty_string_to_none(snapshot.last_assistant_reply), published_profile_id: snapshot.published_profile_id, updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } fn map_square_hole_creator_config( snapshot: SquareHoleCreatorConfigJsonRecord, ) -> SquareHoleCreatorConfigRecord { SquareHoleCreatorConfigRecord { theme_text: snapshot.theme_text, twist_rule: snapshot.twist_rule, shape_count: snapshot.shape_count, difficulty: snapshot.difficulty, shape_options: snapshot .shape_options .into_iter() .map(map_square_hole_shape_option) .collect(), hole_options: snapshot .hole_options .into_iter() .map(map_square_hole_hole_option) .collect(), background_prompt: snapshot.background_prompt, cover_image_src: empty_string_to_none(snapshot.cover_image_src), background_image_src: empty_string_to_none(snapshot.background_image_src), } } fn map_square_hole_result_draft( snapshot: SquareHoleDraftJsonRecord, ) -> SquareHoleResultDraftRecord { SquareHoleResultDraftRecord { profile_id: snapshot.profile_id, game_name: snapshot.game_name, theme_text: snapshot.theme_text, twist_rule: snapshot.twist_rule, summary: snapshot.summary_text, tags: snapshot.tags, cover_image_src: empty_string_to_none(snapshot.cover_image_src), background_prompt: snapshot.background_prompt, background_image_src: empty_string_to_none(snapshot.background_image_src), shape_options: snapshot .shape_options .into_iter() .map(map_square_hole_shape_option) .collect(), hole_options: snapshot .hole_options .into_iter() .map(map_square_hole_hole_option) .collect(), shape_count: snapshot.shape_count, difficulty: snapshot.difficulty, publish_ready: false, blockers: Vec::new(), } } fn map_square_hole_agent_message_snapshot( snapshot: SquareHoleAgentMessageJsonRecord, ) -> SquareHoleAgentMessageRecord { SquareHoleAgentMessageRecord { id: snapshot.message_id, role: snapshot.role, kind: normalize_square_hole_message_kind(&snapshot.kind).to_string(), text: snapshot.text, created_at: format_timestamp_micros(snapshot.created_at_micros), } } fn map_square_hole_work_snapshot( snapshot: SquareHoleWorkJsonRecord, ) -> SquareHoleWorkProfileRecord { SquareHoleWorkProfileRecord { work_id: snapshot.work_id, profile_id: snapshot.profile_id, owner_user_id: snapshot.owner_user_id, source_session_id: empty_string_to_none(snapshot.source_session_id), author_display_name: snapshot.author_display_name, game_name: snapshot.game_name, theme_text: snapshot.theme_text, twist_rule: snapshot.twist_rule, summary: snapshot.summary_text, tags: snapshot.tags, cover_image_src: empty_string_to_none(snapshot.cover_image_src), background_prompt: snapshot.background_prompt, background_image_src: empty_string_to_none(snapshot.background_image_src), shape_options: snapshot .shape_options .into_iter() .map(map_square_hole_shape_option) .collect(), hole_options: snapshot .hole_options .into_iter() .map(map_square_hole_hole_option) .collect(), shape_count: snapshot.shape_count, difficulty: snapshot.difficulty, publication_status: normalize_square_hole_publication_status(&snapshot.publication_status) .to_string(), play_count: snapshot.play_count, updated_at: format_timestamp_micros(snapshot.updated_at_micros), published_at: snapshot.published_at_micros.map(format_timestamp_micros), publish_ready: snapshot.publish_ready, } } fn map_square_hole_run_json(run_json: String) -> Result { let run = serde_json::from_str::(&run_json).map_err(|error| { SpacetimeClientError::Runtime(format!("square hole run_json 非法: {error}")) })?; Ok(map_square_hole_run_snapshot(run)) } fn map_square_hole_run_snapshot(snapshot: SquareHoleRunJsonRecord) -> SquareHoleRunRecord { SquareHoleRunRecord { run_id: snapshot.run_id, profile_id: snapshot.profile_id, owner_user_id: snapshot.owner_user_id, status: normalize_square_hole_run_status(&snapshot.status).to_string(), snapshot_version: snapshot.snapshot_version, started_at_ms: i64_to_u64_ms(snapshot.started_at_ms), duration_limit_ms: i64_to_u64_ms(snapshot.duration_limit_ms), server_now_ms: Some(i64_to_u64_ms(snapshot.server_now_ms)), remaining_ms: i64_to_u64_ms(snapshot.remaining_ms), total_shape_count: snapshot.total_shape_count, completed_shape_count: snapshot.completed_shape_count, combo: snapshot.combo, best_combo: snapshot.best_combo, score: snapshot.score, rule_label: snapshot.rule_label, background_image_src: empty_string_to_none(snapshot.background_image_src), current_shape: snapshot.current_shape.map(map_square_hole_shape_snapshot), holes: snapshot .holes .into_iter() .map(map_square_hole_hole_snapshot) .collect(), last_feedback: snapshot .last_feedback .map(map_square_hole_feedback_snapshot), last_confirmed_action_id: None, } } fn map_square_hole_shape_snapshot( snapshot: SquareHoleShapeJsonRecord, ) -> SquareHoleShapeSnapshotRecord { SquareHoleShapeSnapshotRecord { shape_id: snapshot.shape_id, shape_kind: snapshot.shape_kind, label: snapshot.label, target_hole_id: snapshot.target_hole_id, color: snapshot.color, image_src: empty_string_to_none(snapshot.image_src), } } fn map_square_hole_hole_snapshot( snapshot: SquareHoleHoleJsonRecord, ) -> SquareHoleHoleSnapshotRecord { SquareHoleHoleSnapshotRecord { hole_id: snapshot.hole_id, hole_kind: snapshot.hole_kind, label: snapshot.label, x: snapshot.x, y: snapshot.y, image_src: empty_string_to_none(snapshot.image_src), } } fn map_square_hole_shape_option( snapshot: SquareHoleShapeOptionJsonRecord, ) -> SquareHoleShapeOptionRecord { SquareHoleShapeOptionRecord { option_id: snapshot.option_id, shape_kind: snapshot.shape_kind, label: snapshot.label, target_hole_id: snapshot.target_hole_id, image_prompt: snapshot.image_prompt, image_src: empty_string_to_none(snapshot.image_src), } } fn map_square_hole_hole_option( snapshot: SquareHoleHoleOptionJsonRecord, ) -> SquareHoleHoleOptionRecord { SquareHoleHoleOptionRecord { hole_id: snapshot.hole_id, hole_kind: snapshot.hole_kind, label: snapshot.label, image_prompt: snapshot.image_prompt, image_src: empty_string_to_none(snapshot.image_src), } } fn map_square_hole_feedback_snapshot( snapshot: SquareHoleDropFeedbackJsonRecord, ) -> SquareHoleDropFeedbackRecord { SquareHoleDropFeedbackRecord { accepted: snapshot.accepted, reject_reason: snapshot .reject_reason .map(|value| normalize_square_hole_reject_reason(&value).to_string()), message: snapshot.message, } } fn build_square_hole_anchor_pack( config: &SquareHoleCreatorConfigRecord, ) -> SquareHoleAnchorPackRecord { let shape_count = config.shape_count.to_string(); let difficulty = config.difficulty.to_string(); SquareHoleAnchorPackRecord { theme: build_square_hole_anchor_item("theme", "题材主题", config.theme_text.as_str()), twist_rule: build_square_hole_anchor_item( "twistRule", "反差规则", config.twist_rule.as_str(), ), shape_count: build_square_hole_anchor_item("shapeCount", "形状数量", shape_count.as_str()), difficulty: build_square_hole_anchor_item("difficulty", "难度", difficulty.as_str()), } } fn build_square_hole_anchor_item( key: &str, label: &str, value: &str, ) -> SquareHoleAnchorItemRecord { SquareHoleAnchorItemRecord { key: key.to_string(), label: label.to_string(), value: value.to_string(), status: if value.trim().is_empty() { "missing" } else { "confirmed" } .to_string(), } } fn map_visual_novel_agent_session_snapshot( snapshot: VisualNovelAgentSessionJsonRecord, ) -> VisualNovelAgentSessionRecord { VisualNovelAgentSessionRecord { session_id: snapshot.session_id, owner_user_id: snapshot.owner_user_id, source_mode: snapshot.source_mode, status: snapshot.status, seed_text: snapshot.seed_text, source_asset_ids: snapshot.source_asset_ids, current_turn: snapshot.current_turn, progress_percent: snapshot.progress_percent, messages: snapshot .messages .into_iter() .map(map_visual_novel_agent_message) .collect(), draft: snapshot.draft, pending_action: snapshot.pending_action, last_assistant_reply: snapshot.last_assistant_reply, published_profile_id: snapshot.published_profile_id, created_at: format_timestamp_micros(snapshot.created_at_micros), updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } fn map_visual_novel_agent_message( snapshot: VisualNovelAgentMessageJsonRecord, ) -> VisualNovelAgentMessageRecord { VisualNovelAgentMessageRecord { message_id: snapshot.message_id, session_id: snapshot.session_id, role: snapshot.role, kind: snapshot.kind, text: snapshot.text, created_at: format_timestamp_micros(snapshot.created_at_micros), } } fn map_visual_novel_work_snapshot( snapshot: VisualNovelWorkJsonRecord, ) -> VisualNovelWorkProfileRecord { VisualNovelWorkProfileRecord { work_id: snapshot.work_id, profile_id: snapshot.profile_id, owner_user_id: snapshot.owner_user_id, source_session_id: snapshot.source_session_id, author_display_name: snapshot.author_display_name, work_title: snapshot.work_title, work_description: snapshot.work_description, tags: snapshot.tags, cover_image_src: snapshot.cover_image_src, source_asset_ids: snapshot.source_asset_ids, draft: snapshot.draft, publication_status: snapshot.publication_status, publish_ready: snapshot.publish_ready, play_count: snapshot.play_count, created_at: format_timestamp_micros(snapshot.created_at_micros), updated_at: format_timestamp_micros(snapshot.updated_at_micros), published_at: snapshot.published_at_micros.map(format_timestamp_micros), } } fn map_visual_novel_run_snapshot(snapshot: VisualNovelRunJsonRecord) -> VisualNovelRunRecord { VisualNovelRunRecord { run_id: snapshot.run_id, owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, mode: snapshot.mode, status: snapshot.status, current_scene_id: snapshot.current_scene_id, current_phase_id: snapshot.current_phase_id, visible_character_ids: snapshot.visible_character_ids, flags: snapshot.flags, metrics: snapshot.metrics, history: snapshot .history .into_iter() .map(map_visual_novel_history_entry) .collect(), available_choices: snapshot.available_choices, text_mode_enabled: snapshot.text_mode_enabled, created_at: format_timestamp_micros(snapshot.created_at_micros), updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } fn map_visual_novel_history_entry( snapshot: VisualNovelHistoryEntryJsonRecord, ) -> VisualNovelHistoryEntryRecord { VisualNovelHistoryEntryRecord { entry_id: snapshot.entry_id, run_id: snapshot.run_id, owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, turn_index: snapshot.turn_index, source: snapshot.source, action_text: snapshot.action_text, steps: snapshot.steps, snapshot_before_hash: snapshot.snapshot_before_hash, snapshot_after_hash: snapshot.snapshot_after_hash, created_at: format_timestamp_micros(snapshot.created_at_micros), } } fn map_visual_novel_runtime_event( snapshot: VisualNovelRuntimeEventJsonRecord, ) -> VisualNovelRuntimeEventRecord { VisualNovelRuntimeEventRecord { event_id: snapshot.event_id, run_id: snapshot.run_id, owner_user_id: snapshot.owner_user_id, profile_id: snapshot.profile_id, event_kind: snapshot.event_kind, client_event_id: snapshot.client_event_id, history_entry_id: snapshot.history_entry_id, payload: snapshot.payload, occurred_at: format_timestamp_micros(snapshot.occurred_at_micros), } } fn normalize_match3d_stage(value: &str) -> &str { match value { "Collecting" | "collecting" | "collecting_config" => "collecting_config", "ReadyToCompile" | "ready_to_compile" => "ready_to_compile", "DraftCompiled" | "draft_compiled" | "draft_ready" => "draft_ready", "Published" | "published" => "published", _ => value, } } fn normalize_match3d_publication_status(value: &str) -> &str { match value { "Draft" | "draft" => "draft", "Published" | "published" => "published", _ => value, } } fn normalize_match3d_message_kind(value: &str) -> &str { match value { "text" => "chat", _ => value, } } fn normalize_square_hole_stage(value: &str) -> &str { match value { "Collecting" | "CollectingConfig" | "collecting" | "collecting_config" => { "collecting_config" } "ReadyToCompile" | "ready_to_compile" => "ready_to_compile", "DraftCompiled" | "DraftReady" | "draft_compiled" | "draft_ready" => "draft_ready", "Published" | "published" => "published", _ => value, } } fn normalize_square_hole_publication_status(value: &str) -> &str { match value { "Draft" | "draft" => "draft", "Published" | "published" => "published", _ => value, } } fn normalize_square_hole_run_status(value: &str) -> &str { match value { "Running" | "running" => "running", "Won" | "won" => "won", "Failed" | "failed" => "failed", "Stopped" | "stopped" => "stopped", _ => value, } } fn normalize_square_hole_message_kind(value: &str) -> &str { match value { "text" => "chat", _ => value, } } fn normalize_square_hole_reject_reason(value: &str) -> &str { match value { "RunNotActive" | "run_not_active" => "run_not_active", "SnapshotVersionMismatch" | "snapshot_version_mismatch" => "snapshot_version_mismatch", "HoleNotFound" | "hole_not_found" => "hole_not_found", "Incompatible" | "incompatible" => "incompatible", "TimeUp" | "time_up" => "time_up", _ => value, } } fn empty_string_to_none(value: String) -> Option { let trimmed = value.trim(); if trimmed.is_empty() { None } else { Some(trimmed.to_string()) } } fn i64_to_u64_ms(value: i64) -> u64 { value.max(0) as u64 } pub(crate) fn map_puzzle_suggested_action( snapshot: DomainPuzzleAgentSuggestedAction, ) -> PuzzleAgentSuggestedActionRecord { PuzzleAgentSuggestedActionRecord { action_id: snapshot.id, action_type: snapshot.action_type, label: snapshot.label, } } pub(crate) fn map_puzzle_result_preview( snapshot: DomainPuzzleResultPreviewEnvelope, ) -> PuzzleResultPreviewRecord { PuzzleResultPreviewRecord { draft: map_puzzle_result_draft(snapshot.draft), blockers: snapshot .blockers .into_iter() .map(map_puzzle_result_preview_blocker) .collect(), quality_findings: snapshot .quality_findings .into_iter() .map(map_puzzle_result_preview_finding) .collect(), publish_ready: snapshot.publish_ready, } } pub(crate) fn map_puzzle_result_preview_blocker( snapshot: DomainPuzzleResultPreviewBlocker, ) -> PuzzleResultPreviewBlockerRecord { PuzzleResultPreviewBlockerRecord { blocker_id: snapshot.id, code: snapshot.code, message: snapshot.message, } } pub(crate) fn map_puzzle_result_preview_finding( snapshot: DomainPuzzleResultPreviewFinding, ) -> PuzzleResultPreviewFindingRecord { PuzzleResultPreviewFindingRecord { finding_id: snapshot.id, severity: snapshot.severity, code: snapshot.code, message: snapshot.message, } } pub(crate) fn map_puzzle_work_profile( snapshot: DomainPuzzleWorkProfile, ) -> PuzzleWorkProfileRecord { PuzzleWorkProfileRecord { work_id: snapshot.work_id, profile_id: snapshot.profile_id, owner_user_id: snapshot.owner_user_id, source_session_id: snapshot.source_session_id, author_display_name: snapshot.author_display_name, work_title: snapshot.work_title, work_description: snapshot.work_description, level_name: snapshot.level_name, summary: snapshot.summary, theme_tags: snapshot.theme_tags, cover_image_src: snapshot.cover_image_src, cover_asset_id: snapshot.cover_asset_id, publication_status: snapshot.publication_status.as_str().to_string(), updated_at: format_timestamp_micros(snapshot.updated_at_micros), published_at: snapshot.published_at_micros.map(format_timestamp_micros), play_count: snapshot.play_count, remix_count: snapshot.remix_count, like_count: snapshot.like_count, recent_play_count_7d: snapshot.recent_play_count_7d, point_incentive_total_half_points: snapshot.point_incentive_total_half_points, point_incentive_claimed_points: snapshot.point_incentive_claimed_points, publish_ready: snapshot.publish_ready, anchor_pack: map_puzzle_anchor_pack(snapshot.anchor_pack), levels: snapshot .levels .into_iter() .map(map_puzzle_draft_level) .collect(), } } pub(crate) fn map_puzzle_run_snapshot(snapshot: DomainPuzzleRunSnapshot) -> PuzzleRunRecord { PuzzleRunRecord { run_id: snapshot.run_id, entry_profile_id: snapshot.entry_profile_id, cleared_level_count: snapshot.cleared_level_count, current_level_index: snapshot.current_level_index, current_grid_size: snapshot.current_grid_size, played_profile_ids: snapshot.played_profile_ids, previous_level_tags: snapshot.previous_level_tags, current_level: snapshot .current_level .map(map_puzzle_runtime_level_snapshot), recommended_next_profile_id: snapshot.recommended_next_profile_id, next_level_mode: snapshot.next_level_mode, next_level_profile_id: snapshot.next_level_profile_id, next_level_id: snapshot.next_level_id, recommended_next_works: snapshot .recommended_next_works .into_iter() .map(map_puzzle_recommended_next_work) .collect(), leaderboard_entries: snapshot .leaderboard_entries .into_iter() .map(map_puzzle_leaderboard_entry) .collect(), } } fn map_puzzle_recommended_next_work( snapshot: module_puzzle::PuzzleRecommendedNextWork, ) -> PuzzleRecommendedNextWorkRecord { PuzzleRecommendedNextWorkRecord { profile_id: snapshot.profile_id, level_name: snapshot.level_name, author_display_name: snapshot.author_display_name, theme_tags: snapshot.theme_tags, cover_image_src: snapshot.cover_image_src, similarity_score: snapshot.similarity_score, } } pub(crate) fn map_puzzle_runtime_level_snapshot( snapshot: DomainPuzzleRuntimeLevelSnapshot, ) -> PuzzleRuntimeLevelRecord { let started_at_ms = if snapshot.started_at_ms == 0 { // 中文注释:旧 run_json 没有计时字段时只补一个可用开始时间,其余限时字段保持旧默认值。 current_unix_millis_for_legacy_puzzle_snapshot() } else { snapshot.started_at_ms }; PuzzleRuntimeLevelRecord { run_id: snapshot.run_id, level_index: snapshot.level_index, level_id: snapshot.level_id, grid_size: snapshot.grid_size, profile_id: snapshot.profile_id, level_name: snapshot.level_name, author_display_name: snapshot.author_display_name, theme_tags: snapshot.theme_tags, cover_image_src: snapshot.cover_image_src, ui_background_image_src: snapshot.ui_background_image_src, ui_background_image_object_key: snapshot.ui_background_image_object_key, background_music: snapshot.background_music.map(map_puzzle_audio_asset), board: map_puzzle_board_snapshot(snapshot.board), status: snapshot.status.as_str().to_string(), started_at_ms, cleared_at_ms: snapshot.cleared_at_ms, elapsed_ms: snapshot.elapsed_ms, time_limit_ms: snapshot.time_limit_ms, remaining_ms: snapshot.remaining_ms, paused_accumulated_ms: snapshot.paused_accumulated_ms, pause_started_at_ms: snapshot.pause_started_at_ms, freeze_accumulated_ms: snapshot.freeze_accumulated_ms, freeze_started_at_ms: snapshot.freeze_started_at_ms, freeze_until_ms: snapshot.freeze_until_ms, leaderboard_entries: snapshot .leaderboard_entries .into_iter() .map(map_puzzle_leaderboard_entry) .collect(), } } fn current_unix_millis_for_legacy_puzzle_snapshot() -> u64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .map(|duration| duration.as_millis().min(u128::from(u64::MAX)) as u64) .unwrap_or(1) } pub(crate) fn map_puzzle_leaderboard_entry( snapshot: module_puzzle::PuzzleLeaderboardEntry, ) -> PuzzleLeaderboardEntryRecord { PuzzleLeaderboardEntryRecord { rank: snapshot.rank, nickname: snapshot.nickname, elapsed_ms: snapshot.elapsed_ms, visible_tags: snapshot.visible_tags, is_current_player: snapshot.is_current_player, } } pub(crate) fn map_puzzle_board_snapshot(snapshot: DomainPuzzleBoardSnapshot) -> PuzzleBoardRecord { PuzzleBoardRecord { rows: snapshot.rows, cols: snapshot.cols, pieces: snapshot .pieces .into_iter() .map(map_puzzle_piece_state) .collect(), merged_groups: snapshot .merged_groups .into_iter() .map(map_puzzle_merged_group_state) .collect(), selected_piece_id: snapshot.selected_piece_id, all_tiles_resolved: snapshot.all_tiles_resolved, } } pub(crate) fn map_puzzle_piece_state(snapshot: DomainPuzzlePieceState) -> PuzzlePieceStateRecord { PuzzlePieceStateRecord { piece_id: snapshot.piece_id, correct_row: snapshot.correct_row, correct_col: snapshot.correct_col, current_row: snapshot.current_row, current_col: snapshot.current_col, merged_group_id: snapshot.merged_group_id, } } pub(crate) fn map_puzzle_merged_group_state( snapshot: DomainPuzzleMergedGroupState, ) -> PuzzleMergedGroupRecord { PuzzleMergedGroupRecord { group_id: snapshot.group_id, piece_ids: snapshot.piece_ids, occupied_cells: snapshot .occupied_cells .into_iter() .map(map_puzzle_cell_position) .collect(), } } pub(crate) fn map_puzzle_cell_position( snapshot: DomainPuzzleCellPosition, ) -> PuzzleCellPositionRecord { PuzzleCellPositionRecord { row: snapshot.row, col: snapshot.col, } } pub(crate) fn map_big_fish_anchor_pack(snapshot: BigFishAnchorPack) -> BigFishAnchorPackRecord { BigFishAnchorPackRecord { gameplay_promise: map_big_fish_anchor_item(snapshot.gameplay_promise), ecology_visual_theme: map_big_fish_anchor_item(snapshot.ecology_visual_theme), growth_ladder: map_big_fish_anchor_item(snapshot.growth_ladder), risk_tempo: map_big_fish_anchor_item(snapshot.risk_tempo), } } pub(crate) fn map_big_fish_anchor_item(snapshot: BigFishAnchorItem) -> BigFishAnchorItemRecord { BigFishAnchorItemRecord { key: snapshot.key, label: snapshot.label, value: snapshot.value, status: format_big_fish_anchor_status(snapshot.status).to_string(), } } pub(crate) fn map_big_fish_game_draft(snapshot: BigFishGameDraft) -> BigFishGameDraftRecord { BigFishGameDraftRecord { title: snapshot.title, subtitle: snapshot.subtitle, core_fun: snapshot.core_fun, ecology_theme: snapshot.ecology_theme, levels: snapshot .levels .into_iter() .map(map_big_fish_level_blueprint) .collect(), background: map_big_fish_background_blueprint(snapshot.background), runtime_params: map_big_fish_runtime_params(snapshot.runtime_params), } } pub(crate) fn map_big_fish_level_blueprint( snapshot: BigFishLevelBlueprint, ) -> BigFishLevelBlueprintRecord { BigFishLevelBlueprintRecord { level: snapshot.level, name: snapshot.name, one_line_fantasy: snapshot.one_line_fantasy, text_description: snapshot.text_description, silhouette_direction: snapshot.silhouette_direction, size_ratio: snapshot.size_ratio, visual_description: snapshot.visual_description, visual_prompt_seed: snapshot.visual_prompt_seed, idle_motion_description: snapshot.idle_motion_description, move_motion_description: snapshot.move_motion_description, motion_prompt_seed: snapshot.motion_prompt_seed, merge_source_level: snapshot.merge_source_level, prey_window: snapshot.prey_window, threat_window: snapshot.threat_window, is_final_level: snapshot.is_final_level, } } pub(crate) fn map_big_fish_background_blueprint( snapshot: BigFishBackgroundBlueprint, ) -> BigFishBackgroundBlueprintRecord { BigFishBackgroundBlueprintRecord { theme: snapshot.theme, color_mood: snapshot.color_mood, foreground_hints: snapshot.foreground_hints, midground_composition: snapshot.midground_composition, background_depth: snapshot.background_depth, safe_play_area_hint: snapshot.safe_play_area_hint, spawn_edge_hint: snapshot.spawn_edge_hint, background_prompt_seed: snapshot.background_prompt_seed, } } pub(crate) fn map_big_fish_runtime_params( snapshot: BigFishRuntimeParams, ) -> BigFishRuntimeParamsRecord { BigFishRuntimeParamsRecord { level_count: snapshot.level_count, merge_count_per_upgrade: snapshot.merge_count_per_upgrade, spawn_target_count: snapshot.spawn_target_count, leader_move_speed: snapshot.leader_move_speed, follower_catch_up_speed: snapshot.follower_catch_up_speed, offscreen_cull_seconds: snapshot.offscreen_cull_seconds, prey_spawn_delta_levels: snapshot.prey_spawn_delta_levels, threat_spawn_delta_levels: snapshot.threat_spawn_delta_levels, win_level: snapshot.win_level, } } pub(crate) fn map_big_fish_asset_slot_snapshot( snapshot: BigFishAssetSlotSnapshot, ) -> BigFishAssetSlotRecord { BigFishAssetSlotRecord { slot_id: snapshot.slot_id, asset_kind: format_big_fish_asset_kind(snapshot.asset_kind).to_string(), level: snapshot.level, motion_key: snapshot.motion_key, status: format_big_fish_asset_status(snapshot.status).to_string(), asset_url: snapshot.asset_url, prompt_snapshot: snapshot.prompt_snapshot, updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } pub(crate) fn map_big_fish_asset_coverage( snapshot: BigFishAssetCoverage, ) -> BigFishAssetCoverageRecord { BigFishAssetCoverageRecord { level_main_image_ready_count: snapshot.level_main_image_ready_count, level_motion_ready_count: snapshot.level_motion_ready_count, background_ready: snapshot.background_ready, required_level_count: snapshot.required_level_count, publish_ready: snapshot.publish_ready, blockers: snapshot.blockers, } } pub(crate) fn map_big_fish_agent_message_snapshot( snapshot: BigFishAgentMessageSnapshot, ) -> BigFishAgentMessageRecord { BigFishAgentMessageRecord { message_id: snapshot.message_id, role: format_big_fish_agent_message_role(snapshot.role).to_string(), kind: format_big_fish_agent_message_kind(snapshot.kind).to_string(), text: snapshot.text, created_at: format_timestamp_micros(snapshot.created_at_micros), } } pub(crate) fn map_big_fish_runtime_snapshot( snapshot: module_big_fish::BigFishRuntimeSnapshot, ) -> BigFishRuntimeRunRecord { BigFishRuntimeRunRecord { run_id: snapshot.run_id, session_id: snapshot.session_id, status: snapshot.status.as_str().to_string(), tick: snapshot.tick, player_level: snapshot.player_level, win_level: snapshot.win_level, leader_entity_id: snapshot.leader_entity_id, owned_entities: snapshot .owned_entities .into_iter() .map(map_big_fish_runtime_entity_snapshot) .collect(), wild_entities: snapshot .wild_entities .into_iter() .map(map_big_fish_runtime_entity_snapshot) .collect(), camera_center: map_big_fish_vector2(snapshot.camera_center), last_input: map_big_fish_vector2(snapshot.last_input), event_log: snapshot.event_log, updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } fn map_big_fish_runtime_entity_snapshot( snapshot: module_big_fish::BigFishRuntimeEntitySnapshot, ) -> BigFishRuntimeEntityRecord { BigFishRuntimeEntityRecord { entity_id: snapshot.entity_id, level: snapshot.level, position: map_big_fish_vector2(snapshot.position), radius: snapshot.radius, offscreen_seconds: snapshot.offscreen_seconds, } } fn map_big_fish_vector2(snapshot: module_big_fish::BigFishVector2) -> BigFishVector2Record { BigFishVector2Record { x: snapshot.x, y: snapshot.y, } } pub(crate) fn map_story_session_snapshot(snapshot: StorySessionSnapshot) -> StorySessionRecord { StorySessionRecord { story_session_id: snapshot.story_session_id, runtime_session_id: snapshot.runtime_session_id, actor_user_id: snapshot.actor_user_id, world_profile_id: snapshot.world_profile_id, initial_prompt: snapshot.initial_prompt, opening_summary: snapshot.opening_summary, latest_narrative_text: snapshot.latest_narrative_text, latest_choice_function_id: snapshot.latest_choice_function_id, status: map_story_session_status(snapshot.status) .as_str() .to_string(), version: snapshot.version, created_at: format_timestamp_micros(snapshot.created_at_micros), updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } pub(crate) fn map_ai_task_snapshot(snapshot: AiTaskSnapshot) -> AiTaskRecord { AiTaskRecord { task_id: snapshot.task_id, task_kind: format_ai_task_kind(snapshot.task_kind).to_string(), owner_user_id: snapshot.owner_user_id, request_label: snapshot.request_label, source_module: snapshot.source_module, source_entity_id: snapshot.source_entity_id, request_payload_json: snapshot.request_payload_json, status: format_ai_task_status(snapshot.status).to_string(), failure_message: snapshot.failure_message, stages: snapshot .stages .into_iter() .map(map_ai_task_stage_snapshot) .collect(), result_references: snapshot .result_references .into_iter() .map(map_ai_result_reference_snapshot) .collect(), latest_text_output: snapshot.latest_text_output, latest_structured_payload_json: snapshot.latest_structured_payload_json, version: snapshot.version, created_at: format_timestamp_micros(snapshot.created_at_micros), started_at: snapshot.started_at_micros.map(format_timestamp_micros), completed_at: snapshot.completed_at_micros.map(format_timestamp_micros), updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } pub(crate) fn map_ai_task_stage_snapshot(snapshot: AiTaskStageSnapshot) -> AiTaskStageRecord { AiTaskStageRecord { stage_kind: format_ai_task_stage_kind(snapshot.stage_kind).to_string(), label: snapshot.label, detail: snapshot.detail, order: snapshot.order, status: format_ai_task_stage_status(snapshot.status).to_string(), text_output: snapshot.text_output, structured_payload_json: snapshot.structured_payload_json, warning_messages: snapshot.warning_messages, started_at: snapshot.started_at_micros.map(format_timestamp_micros), completed_at: snapshot.completed_at_micros.map(format_timestamp_micros), } } pub(crate) fn map_ai_text_chunk_snapshot(snapshot: AiTextChunkSnapshot) -> AiTextChunkRecord { AiTextChunkRecord { chunk_id: snapshot.chunk_id, task_id: snapshot.task_id, stage_kind: format_ai_task_stage_kind(snapshot.stage_kind).to_string(), sequence: snapshot.sequence, delta_text: snapshot.delta_text, created_at: format_timestamp_micros(snapshot.created_at_micros), } } pub(crate) fn map_ai_result_reference_snapshot( snapshot: AiResultReferenceSnapshot, ) -> AiResultReferenceRecord { AiResultReferenceRecord { result_ref_id: snapshot.result_ref_id, task_id: snapshot.task_id, reference_kind: format_ai_result_reference_kind(snapshot.reference_kind).to_string(), reference_id: snapshot.reference_id, label: snapshot.label, created_at: format_timestamp_micros(snapshot.created_at_micros), } } pub(crate) fn map_story_event_snapshot(snapshot: StoryEventSnapshot) -> StoryEventRecord { StoryEventRecord { event_id: snapshot.event_id, story_session_id: snapshot.story_session_id, event_kind: map_story_event_kind(snapshot.event_kind) .as_str() .to_string(), narrative_text: snapshot.narrative_text, choice_function_id: snapshot.choice_function_id, created_at: format_timestamp_micros(snapshot.created_at_micros), } } pub(crate) fn map_battle_state_snapshot( snapshot: BattleStateSnapshot, ) -> DomainBattleStateSnapshot { DomainBattleStateSnapshot { battle_state_id: snapshot.battle_state_id, story_session_id: snapshot.story_session_id, runtime_session_id: snapshot.runtime_session_id, actor_user_id: snapshot.actor_user_id, chapter_id: snapshot.chapter_id, target_npc_id: snapshot.target_npc_id, target_name: snapshot.target_name, battle_mode: map_battle_mode_back(snapshot.battle_mode), status: map_battle_status(snapshot.status), player_hp: snapshot.player_hp, player_max_hp: snapshot.player_max_hp, player_mana: snapshot.player_mana, player_max_mana: snapshot.player_max_mana, target_hp: snapshot.target_hp, target_max_hp: snapshot.target_max_hp, experience_reward: snapshot.experience_reward, reward_items: snapshot .reward_items .into_iter() .map(map_runtime_item_reward_item_snapshot_back) .collect(), turn_index: snapshot.turn_index, last_action_function_id: snapshot.last_action_function_id, last_action_text: snapshot.last_action_text, last_result_text: snapshot.last_result_text, last_damage_dealt: snapshot.last_damage_dealt, last_damage_taken: snapshot.last_damage_taken, last_outcome: map_combat_outcome(snapshot.last_outcome), version: snapshot.version, created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_inventory_state_snapshot( snapshot: RuntimeInventoryStateSnapshot, ) -> DomainRuntimeInventoryStateSnapshot { DomainRuntimeInventoryStateSnapshot { runtime_session_id: snapshot.runtime_session_id, actor_user_id: snapshot.actor_user_id, backpack_items: snapshot .backpack_items .into_iter() .map(map_inventory_slot_snapshot) .collect(), equipment_items: snapshot .equipment_items .into_iter() .map(map_inventory_slot_snapshot) .collect(), } } pub(crate) fn map_resolve_combat_action_result( result: ResolveCombatActionResult, ) -> DomainResolveCombatActionResult { DomainResolveCombatActionResult { snapshot: map_battle_state_snapshot(result.snapshot), damage_dealt: result.damage_dealt, damage_taken: result.damage_taken, outcome: map_combat_outcome(result.outcome), } } pub(crate) fn map_npc_battle_interaction_result( result: NpcBattleInteractionResult, ) -> NpcBattleInteractionSnapshot { NpcBattleInteractionSnapshot { interaction: map_npc_interaction_result(result.interaction), battle_state: map_battle_state_snapshot(result.battle_state), } } pub(crate) fn map_inventory_slot_snapshot( snapshot: InventorySlotSnapshot, ) -> module_inventory::InventorySlotSnapshot { module_inventory::InventorySlotSnapshot { slot_id: snapshot.slot_id, runtime_session_id: snapshot.runtime_session_id, story_session_id: snapshot.story_session_id, actor_user_id: snapshot.actor_user_id, container_kind: map_inventory_container_kind(snapshot.container_kind), slot_key: snapshot.slot_key, item_id: snapshot.item_id, category: snapshot.category, name: snapshot.name, description: snapshot.description, quantity: snapshot.quantity, rarity: map_inventory_item_rarity(snapshot.rarity), tags: snapshot.tags, stackable: snapshot.stackable, stack_key: snapshot.stack_key, equipment_slot_id: snapshot.equipment_slot_id.map(map_inventory_equipment_slot), source_kind: map_inventory_item_source_kind(snapshot.source_kind), source_reference_id: snapshot.source_reference_id, created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_npc_interaction_result( result: NpcInteractionResult, ) -> DomainNpcInteractionResult { DomainNpcInteractionResult { npc_state: map_npc_state_snapshot(result.npc_state), interaction_status: map_npc_interaction_status(result.interaction_status), action_text: result.action_text, result_text: result.result_text, story_text: result.story_text, battle_mode: result.battle_mode.map(map_npc_interaction_battle_mode), encounter_closed: result.encounter_closed, affinity_changed: result.affinity_changed, previous_affinity: result.previous_affinity, next_affinity: result.next_affinity, } } pub(crate) fn map_npc_state_snapshot(snapshot: NpcStateSnapshot) -> DomainNpcStateSnapshot { DomainNpcStateSnapshot { npc_state_id: snapshot.npc_state_id, runtime_session_id: snapshot.runtime_session_id, npc_id: snapshot.npc_id, npc_name: snapshot.npc_name, affinity: snapshot.affinity, relation_state: map_npc_relation_state(snapshot.relation_state), help_used: snapshot.help_used, chatted_count: snapshot.chatted_count, gifts_given: snapshot.gifts_given, recruited: snapshot.recruited, trade_stock_signature: snapshot.trade_stock_signature, revealed_facts: snapshot.revealed_facts, known_attribute_rumors: snapshot.known_attribute_rumors, first_meaningful_contact_resolved: snapshot.first_meaningful_contact_resolved, seen_backstory_chapter_ids: snapshot.seen_backstory_chapter_ids, stance_profile: map_npc_stance_profile(snapshot.stance_profile), created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_npc_relation_state(value: NpcRelationState) -> DomainNpcRelationState { DomainNpcRelationState { affinity: value.affinity, stance: map_npc_relation_stance(value.stance), } } pub(crate) fn map_npc_stance_profile(value: NpcStanceProfile) -> DomainNpcStanceProfile { DomainNpcStanceProfile { trust: value.trust, warmth: value.warmth, ideological_fit: value.ideological_fit, fear_or_guard: value.fear_or_guard, loyalty: value.loyalty, current_conflict_tag: value.current_conflict_tag, recent_approvals: value.recent_approvals, recent_disapprovals: value.recent_disapprovals, } } pub(crate) fn map_npc_interaction_status( value: NpcInteractionStatus, ) -> DomainNpcInteractionStatus { match value { NpcInteractionStatus::Previewed => DomainNpcInteractionStatus::Previewed, NpcInteractionStatus::Dialogue => DomainNpcInteractionStatus::Dialogue, NpcInteractionStatus::Resolved => DomainNpcInteractionStatus::Resolved, NpcInteractionStatus::Recruited => DomainNpcInteractionStatus::Recruited, NpcInteractionStatus::BattlePending => DomainNpcInteractionStatus::BattlePending, NpcInteractionStatus::Left => DomainNpcInteractionStatus::Left, } } pub(crate) fn map_npc_interaction_battle_mode( value: NpcInteractionBattleMode, ) -> DomainNpcInteractionBattleMode { match value { NpcInteractionBattleMode::Fight => DomainNpcInteractionBattleMode::Fight, NpcInteractionBattleMode::Spar => DomainNpcInteractionBattleMode::Spar, } } pub(crate) fn map_npc_relation_stance(value: NpcRelationStance) -> DomainNpcRelationStance { match value { NpcRelationStance::Hostile => DomainNpcRelationStance::Hostile, NpcRelationStance::Guarded => DomainNpcRelationStance::Guarded, NpcRelationStance::Neutral => DomainNpcRelationStance::Neutral, NpcRelationStance::Cooperative => DomainNpcRelationStance::Cooperative, NpcRelationStance::Bonded => DomainNpcRelationStance::Bonded, } } pub(crate) fn map_access_policy( value: AssetObjectAccessPolicy, ) -> crate::module_bindings::AssetObjectAccessPolicy { match value { AssetObjectAccessPolicy::Private => { crate::module_bindings::AssetObjectAccessPolicy::Private } AssetObjectAccessPolicy::PublicRead => { crate::module_bindings::AssetObjectAccessPolicy::PublicRead } } } pub(crate) fn map_access_policy_back( value: crate::module_bindings::AssetObjectAccessPolicy, ) -> AssetObjectAccessPolicy { match value { crate::module_bindings::AssetObjectAccessPolicy::Private => { AssetObjectAccessPolicy::Private } crate::module_bindings::AssetObjectAccessPolicy::PublicRead => { AssetObjectAccessPolicy::PublicRead } } } pub(crate) fn map_runtime_platform_theme( value: DomainRuntimePlatformTheme, ) -> crate::module_bindings::RuntimePlatformTheme { match value { DomainRuntimePlatformTheme::Light => crate::module_bindings::RuntimePlatformTheme::Light, DomainRuntimePlatformTheme::Dark => crate::module_bindings::RuntimePlatformTheme::Dark, } } pub(crate) fn map_runtime_item_reward_item_rarity( value: DomainRuntimeItemRewardItemRarity, ) -> RuntimeItemRewardItemRarity { match value { DomainRuntimeItemRewardItemRarity::Common => RuntimeItemRewardItemRarity::Common, DomainRuntimeItemRewardItemRarity::Uncommon => RuntimeItemRewardItemRarity::Uncommon, DomainRuntimeItemRewardItemRarity::Rare => RuntimeItemRewardItemRarity::Rare, DomainRuntimeItemRewardItemRarity::Epic => RuntimeItemRewardItemRarity::Epic, DomainRuntimeItemRewardItemRarity::Legendary => RuntimeItemRewardItemRarity::Legendary, } } pub(crate) fn map_runtime_item_equipment_slot( value: DomainRuntimeItemEquipmentSlot, ) -> RuntimeItemEquipmentSlot { match value { DomainRuntimeItemEquipmentSlot::Weapon => RuntimeItemEquipmentSlot::Weapon, DomainRuntimeItemEquipmentSlot::Armor => RuntimeItemEquipmentSlot::Armor, DomainRuntimeItemEquipmentSlot::Relic => RuntimeItemEquipmentSlot::Relic, } } pub(crate) fn map_custom_world_theme_mode( value: DomainCustomWorldThemeMode, ) -> CustomWorldThemeMode { match value { DomainCustomWorldThemeMode::Martial => CustomWorldThemeMode::Martial, DomainCustomWorldThemeMode::Arcane => CustomWorldThemeMode::Arcane, DomainCustomWorldThemeMode::Machina => CustomWorldThemeMode::Machina, DomainCustomWorldThemeMode::Tide => CustomWorldThemeMode::Tide, DomainCustomWorldThemeMode::Rift => CustomWorldThemeMode::Rift, DomainCustomWorldThemeMode::Mythic => CustomWorldThemeMode::Mythic, } } pub(crate) fn map_battle_mode(value: DomainBattleMode) -> BattleMode { match value { DomainBattleMode::Fight => BattleMode::Fight, DomainBattleMode::Spar => BattleMode::Spar, } } pub(crate) fn map_runtime_platform_theme_back( value: crate::module_bindings::RuntimePlatformTheme, ) -> DomainRuntimePlatformTheme { match value { crate::module_bindings::RuntimePlatformTheme::Light => DomainRuntimePlatformTheme::Light, crate::module_bindings::RuntimePlatformTheme::Dark => DomainRuntimePlatformTheme::Dark, } } pub(crate) fn map_runtime_item_reward_item_rarity_back( value: RuntimeItemRewardItemRarity, ) -> DomainRuntimeItemRewardItemRarity { match value { RuntimeItemRewardItemRarity::Common => DomainRuntimeItemRewardItemRarity::Common, RuntimeItemRewardItemRarity::Uncommon => DomainRuntimeItemRewardItemRarity::Uncommon, RuntimeItemRewardItemRarity::Rare => DomainRuntimeItemRewardItemRarity::Rare, RuntimeItemRewardItemRarity::Epic => DomainRuntimeItemRewardItemRarity::Epic, RuntimeItemRewardItemRarity::Legendary => DomainRuntimeItemRewardItemRarity::Legendary, } } pub(crate) fn map_runtime_item_equipment_slot_back( value: RuntimeItemEquipmentSlot, ) -> DomainRuntimeItemEquipmentSlot { match value { RuntimeItemEquipmentSlot::Weapon => DomainRuntimeItemEquipmentSlot::Weapon, RuntimeItemEquipmentSlot::Armor => DomainRuntimeItemEquipmentSlot::Armor, RuntimeItemEquipmentSlot::Relic => DomainRuntimeItemEquipmentSlot::Relic, } } pub(crate) fn map_custom_world_theme_mode_back( value: CustomWorldThemeMode, ) -> DomainCustomWorldThemeMode { match value { CustomWorldThemeMode::Martial => DomainCustomWorldThemeMode::Martial, CustomWorldThemeMode::Arcane => DomainCustomWorldThemeMode::Arcane, CustomWorldThemeMode::Machina => DomainCustomWorldThemeMode::Machina, CustomWorldThemeMode::Tide => DomainCustomWorldThemeMode::Tide, CustomWorldThemeMode::Rift => DomainCustomWorldThemeMode::Rift, CustomWorldThemeMode::Mythic => DomainCustomWorldThemeMode::Mythic, } } pub(crate) fn map_custom_world_publication_status( value: CustomWorldPublicationStatus, ) -> &'static str { match value { CustomWorldPublicationStatus::Draft => "draft", CustomWorldPublicationStatus::Published => "published", } } pub(crate) fn map_rpg_agent_stage(value: crate::module_bindings::RpgAgentStage) -> String { match value { crate::module_bindings::RpgAgentStage::CollectingIntent => "collecting_intent", crate::module_bindings::RpgAgentStage::Clarifying => "clarifying", crate::module_bindings::RpgAgentStage::FoundationReview => "foundation_review", crate::module_bindings::RpgAgentStage::ObjectRefining => "object_refining", crate::module_bindings::RpgAgentStage::VisualRefining => "visual_refining", crate::module_bindings::RpgAgentStage::LongTailReview => "long_tail_review", crate::module_bindings::RpgAgentStage::ReadyToPublish => "ready_to_publish", crate::module_bindings::RpgAgentStage::Published => "published", crate::module_bindings::RpgAgentStage::Error => "error", } .to_string() } pub(crate) fn parse_puzzle_agent_stage_record( value: &str, ) -> Result { match value.trim() { "collecting_anchors" => Ok(crate::module_bindings::PuzzleAgentStage::CollectingAnchors), "draft_ready" => Ok(crate::module_bindings::PuzzleAgentStage::DraftReady), "image_refining" => Ok(crate::module_bindings::PuzzleAgentStage::ImageRefining), "ready_to_publish" => Ok(crate::module_bindings::PuzzleAgentStage::ReadyToPublish), "published" => Ok(crate::module_bindings::PuzzleAgentStage::Published), other => Err(SpacetimeClientError::Runtime(format!( "未知 puzzle agent stage: {other}" ))), } } pub(crate) fn parse_rpg_agent_stage_record( value: &str, ) -> Result { match value.trim() { "collecting_intent" => Ok(crate::module_bindings::RpgAgentStage::CollectingIntent), "clarifying" => Ok(crate::module_bindings::RpgAgentStage::Clarifying), "foundation_review" => Ok(crate::module_bindings::RpgAgentStage::FoundationReview), "object_refining" => Ok(crate::module_bindings::RpgAgentStage::ObjectRefining), "visual_refining" => Ok(crate::module_bindings::RpgAgentStage::VisualRefining), "long_tail_review" => Ok(crate::module_bindings::RpgAgentStage::LongTailReview), "ready_to_publish" => Ok(crate::module_bindings::RpgAgentStage::ReadyToPublish), "published" => Ok(crate::module_bindings::RpgAgentStage::Published), "error" => Ok(crate::module_bindings::RpgAgentStage::Error), other => Err(SpacetimeClientError::Runtime(format!( "未知 rpg agent stage: {other}" ))), } } pub(crate) fn format_rpg_agent_message_role( value: crate::module_bindings::RpgAgentMessageRole, ) -> &'static str { match value { crate::module_bindings::RpgAgentMessageRole::User => "user", crate::module_bindings::RpgAgentMessageRole::Assistant => "assistant", crate::module_bindings::RpgAgentMessageRole::System => "system", } } pub(crate) fn format_rpg_agent_message_kind( value: crate::module_bindings::RpgAgentMessageKind, ) -> &'static str { match value { crate::module_bindings::RpgAgentMessageKind::Chat => "chat", crate::module_bindings::RpgAgentMessageKind::Clarification => "clarification", crate::module_bindings::RpgAgentMessageKind::Summary => "summary", crate::module_bindings::RpgAgentMessageKind::Checkpoint => "checkpoint", crate::module_bindings::RpgAgentMessageKind::Warning => "warning", crate::module_bindings::RpgAgentMessageKind::ActionResult => "action_result", } } pub(crate) fn format_rpg_agent_operation_type( value: crate::module_bindings::RpgAgentOperationType, ) -> &'static str { match value { crate::module_bindings::RpgAgentOperationType::ProcessMessage => "process_message", crate::module_bindings::RpgAgentOperationType::DraftFoundation => "draft_foundation", crate::module_bindings::RpgAgentOperationType::UpdateDraftCard => "update_draft_card", crate::module_bindings::RpgAgentOperationType::SyncResultProfile => "sync_result_profile", crate::module_bindings::RpgAgentOperationType::GenerateCharacters => "generate_characters", crate::module_bindings::RpgAgentOperationType::GenerateLandmarks => "generate_landmarks", crate::module_bindings::RpgAgentOperationType::GenerateRoleAssets => "generate_role_assets", crate::module_bindings::RpgAgentOperationType::SyncRoleAssets => "sync_role_assets", crate::module_bindings::RpgAgentOperationType::GenerateSceneAssets => { "generate_scene_assets" } crate::module_bindings::RpgAgentOperationType::SyncSceneAssets => "sync_scene_assets", crate::module_bindings::RpgAgentOperationType::ExpandLongTail => "expand_long_tail", crate::module_bindings::RpgAgentOperationType::PublishWorld => "publish_world", crate::module_bindings::RpgAgentOperationType::RevertCheckpoint => "revert_checkpoint", crate::module_bindings::RpgAgentOperationType::DeleteCharacters => "delete_characters", crate::module_bindings::RpgAgentOperationType::DeleteLandmarks => "delete_landmarks", } } pub(crate) fn parse_rpg_agent_operation_type_record( value: &str, ) -> Result { match value.trim() { "process_message" => Ok(crate::module_bindings::RpgAgentOperationType::ProcessMessage), "draft_foundation" => Ok(crate::module_bindings::RpgAgentOperationType::DraftFoundation), "update_draft_card" => Ok(crate::module_bindings::RpgAgentOperationType::UpdateDraftCard), "sync_result_profile" => { Ok(crate::module_bindings::RpgAgentOperationType::SyncResultProfile) } "generate_characters" => { Ok(crate::module_bindings::RpgAgentOperationType::GenerateCharacters) } "generate_landmarks" => { Ok(crate::module_bindings::RpgAgentOperationType::GenerateLandmarks) } "generate_role_assets" => { Ok(crate::module_bindings::RpgAgentOperationType::GenerateRoleAssets) } "sync_role_assets" => Ok(crate::module_bindings::RpgAgentOperationType::SyncRoleAssets), "generate_scene_assets" => { Ok(crate::module_bindings::RpgAgentOperationType::GenerateSceneAssets) } "sync_scene_assets" => Ok(crate::module_bindings::RpgAgentOperationType::SyncSceneAssets), "expand_long_tail" => Ok(crate::module_bindings::RpgAgentOperationType::ExpandLongTail), "publish_world" => Ok(crate::module_bindings::RpgAgentOperationType::PublishWorld), "revert_checkpoint" => Ok(crate::module_bindings::RpgAgentOperationType::RevertCheckpoint), "delete_characters" => Ok(crate::module_bindings::RpgAgentOperationType::DeleteCharacters), "delete_landmarks" => Ok(crate::module_bindings::RpgAgentOperationType::DeleteLandmarks), other => Err(SpacetimeClientError::Runtime(format!( "未知 rpg agent operation type: {other}" ))), } } pub(crate) fn format_rpg_agent_operation_status( value: crate::module_bindings::RpgAgentOperationStatus, ) -> &'static str { match value { crate::module_bindings::RpgAgentOperationStatus::Queued => "queued", crate::module_bindings::RpgAgentOperationStatus::Running => "running", crate::module_bindings::RpgAgentOperationStatus::Completed => "completed", crate::module_bindings::RpgAgentOperationStatus::Failed => "failed", } } pub(crate) fn parse_rpg_agent_operation_status_record( value: &str, ) -> Result { match value.trim() { "queued" => Ok(crate::module_bindings::RpgAgentOperationStatus::Queued), "running" => Ok(crate::module_bindings::RpgAgentOperationStatus::Running), "completed" => Ok(crate::module_bindings::RpgAgentOperationStatus::Completed), "failed" => Ok(crate::module_bindings::RpgAgentOperationStatus::Failed), other => Err(SpacetimeClientError::Runtime(format!( "未知 rpg agent operation status: {other}" ))), } } pub(crate) fn format_rpg_agent_draft_card_kind( value: crate::module_bindings::RpgAgentDraftCardKind, ) -> &'static str { match value { crate::module_bindings::RpgAgentDraftCardKind::World => "world", crate::module_bindings::RpgAgentDraftCardKind::Camp => "camp", crate::module_bindings::RpgAgentDraftCardKind::Faction => "faction", crate::module_bindings::RpgAgentDraftCardKind::Character => "character", crate::module_bindings::RpgAgentDraftCardKind::Landmark => "landmark", crate::module_bindings::RpgAgentDraftCardKind::Thread => "thread", crate::module_bindings::RpgAgentDraftCardKind::Chapter => "chapter", crate::module_bindings::RpgAgentDraftCardKind::SceneChapter => "scene_chapter", crate::module_bindings::RpgAgentDraftCardKind::Carrier => "carrier", crate::module_bindings::RpgAgentDraftCardKind::SidequestSeed => "sidequest_seed", } } pub(crate) fn format_rpg_agent_draft_card_status( value: crate::module_bindings::RpgAgentDraftCardStatus, ) -> &'static str { match value { crate::module_bindings::RpgAgentDraftCardStatus::Suggested => "suggested", crate::module_bindings::RpgAgentDraftCardStatus::Confirmed => "confirmed", crate::module_bindings::RpgAgentDraftCardStatus::Locked => "locked", crate::module_bindings::RpgAgentDraftCardStatus::Warning => "warning", } } pub(crate) fn format_custom_world_role_asset_status_back( value: crate::module_bindings::CustomWorldRoleAssetStatus, ) -> String { match value { crate::module_bindings::CustomWorldRoleAssetStatus::Missing => "missing", crate::module_bindings::CustomWorldRoleAssetStatus::VisualReady => "visual_ready", crate::module_bindings::CustomWorldRoleAssetStatus::AnimationsReady => "animations_ready", crate::module_bindings::CustomWorldRoleAssetStatus::Complete => "complete", } .to_string() } impl TryFrom<&str> for BigFishAssetKind { type Error = SpacetimeClientError; fn try_from(value: &str) -> Result { match value.trim() { "level_main_image" => Ok(Self::LevelMainImage), "level_motion" => Ok(Self::LevelMotion), "stage_background" => Ok(Self::StageBackground), other => Err(SpacetimeClientError::Runtime(format!( "big fish asset kind `{other}` 当前尚未支持" ))), } } } pub(crate) fn parse_big_fish_creation_stage( value: &str, ) -> Result { match value.trim() { "collecting_anchors" => Ok(BigFishCreationStage::CollectingAnchors), "draft_ready" => Ok(BigFishCreationStage::DraftReady), "asset_refining" => Ok(BigFishCreationStage::AssetRefining), "ready_to_publish" => Ok(BigFishCreationStage::ReadyToPublish), "published" => Ok(BigFishCreationStage::Published), other => Err(SpacetimeClientError::Runtime(format!( "big fish creation stage `{other}` 当前尚未支持" ))), } } pub(crate) fn format_big_fish_creation_stage(value: BigFishCreationStage) -> &'static str { match value { BigFishCreationStage::CollectingAnchors => "collecting_anchors", BigFishCreationStage::DraftReady => "draft_ready", BigFishCreationStage::AssetRefining => "asset_refining", BigFishCreationStage::ReadyToPublish => "ready_to_publish", BigFishCreationStage::Published => "published", } } pub(crate) fn format_big_fish_anchor_status(value: BigFishAnchorStatus) -> &'static str { match value { BigFishAnchorStatus::Confirmed => "confirmed", BigFishAnchorStatus::Inferred => "inferred", BigFishAnchorStatus::Missing => "missing", BigFishAnchorStatus::Locked => "locked", } } pub(crate) fn format_big_fish_agent_message_role(value: BigFishAgentMessageRole) -> &'static str { match value { BigFishAgentMessageRole::User => "user", BigFishAgentMessageRole::Assistant => "assistant", BigFishAgentMessageRole::System => "system", } } pub(crate) fn format_big_fish_agent_message_kind(value: BigFishAgentMessageKind) -> &'static str { match value { BigFishAgentMessageKind::Chat => "chat", BigFishAgentMessageKind::Summary => "summary", BigFishAgentMessageKind::ActionResult => "action_result", BigFishAgentMessageKind::Warning => "warning", } } pub(crate) fn format_big_fish_asset_kind(value: BigFishAssetKind) -> &'static str { match value { BigFishAssetKind::LevelMainImage => "level_main_image", BigFishAssetKind::LevelMotion => "level_motion", BigFishAssetKind::StageBackground => "stage_background", } } pub(crate) fn format_big_fish_asset_status(value: BigFishAssetStatus) -> &'static str { match value { BigFishAssetStatus::Missing => "missing", BigFishAssetStatus::Ready => "ready", } } pub(crate) fn format_custom_world_theme_mode(value: DomainCustomWorldThemeMode) -> &'static str { match value { DomainCustomWorldThemeMode::Martial => "martial", DomainCustomWorldThemeMode::Arcane => "arcane", DomainCustomWorldThemeMode::Machina => "machina", DomainCustomWorldThemeMode::Tide => "tide", DomainCustomWorldThemeMode::Rift => "rift", DomainCustomWorldThemeMode::Mythic => "mythic", } } pub(crate) fn map_battle_mode_back(value: BattleMode) -> DomainBattleMode { match value { BattleMode::Fight => DomainBattleMode::Fight, BattleMode::Spar => DomainBattleMode::Spar, } } pub(crate) fn map_runtime_browse_history_theme_mode_back( value: crate::module_bindings::RuntimeBrowseHistoryThemeMode, ) -> module_runtime::RuntimeBrowseHistoryThemeMode { match value { crate::module_bindings::RuntimeBrowseHistoryThemeMode::Martial => { module_runtime::RuntimeBrowseHistoryThemeMode::Martial } crate::module_bindings::RuntimeBrowseHistoryThemeMode::Arcane => { module_runtime::RuntimeBrowseHistoryThemeMode::Arcane } crate::module_bindings::RuntimeBrowseHistoryThemeMode::Machina => { module_runtime::RuntimeBrowseHistoryThemeMode::Machina } crate::module_bindings::RuntimeBrowseHistoryThemeMode::Tide => { module_runtime::RuntimeBrowseHistoryThemeMode::Tide } crate::module_bindings::RuntimeBrowseHistoryThemeMode::Rift => { module_runtime::RuntimeBrowseHistoryThemeMode::Rift } crate::module_bindings::RuntimeBrowseHistoryThemeMode::Mythic => { module_runtime::RuntimeBrowseHistoryThemeMode::Mythic } } } pub(crate) fn map_runtime_profile_wallet_ledger_source_type_back( value: crate::module_bindings::RuntimeProfileWalletLedgerSourceType, ) -> module_runtime::RuntimeProfileWalletLedgerSourceType { match value { crate::module_bindings::RuntimeProfileWalletLedgerSourceType::SnapshotSync => { module_runtime::RuntimeProfileWalletLedgerSourceType::SnapshotSync } crate::module_bindings::RuntimeProfileWalletLedgerSourceType::NewUserRegistrationReward => { module_runtime::RuntimeProfileWalletLedgerSourceType::NewUserRegistrationReward } crate::module_bindings::RuntimeProfileWalletLedgerSourceType::InviteInviterReward => { module_runtime::RuntimeProfileWalletLedgerSourceType::InviteInviterReward } crate::module_bindings::RuntimeProfileWalletLedgerSourceType::InviteInviteeReward => { module_runtime::RuntimeProfileWalletLedgerSourceType::InviteInviteeReward } crate::module_bindings::RuntimeProfileWalletLedgerSourceType::PointsRecharge => { module_runtime::RuntimeProfileWalletLedgerSourceType::PointsRecharge } crate::module_bindings::RuntimeProfileWalletLedgerSourceType::AssetOperationConsume => { module_runtime::RuntimeProfileWalletLedgerSourceType::AssetOperationConsume } crate::module_bindings::RuntimeProfileWalletLedgerSourceType::AssetOperationRefund => { module_runtime::RuntimeProfileWalletLedgerSourceType::AssetOperationRefund } crate::module_bindings::RuntimeProfileWalletLedgerSourceType::RedeemCodeReward => { module_runtime::RuntimeProfileWalletLedgerSourceType::RedeemCodeReward } crate::module_bindings::RuntimeProfileWalletLedgerSourceType::PuzzleAuthorIncentiveClaim => { module_runtime::RuntimeProfileWalletLedgerSourceType::PuzzleAuthorIncentiveClaim } crate::module_bindings::RuntimeProfileWalletLedgerSourceType::DailyTaskReward => { module_runtime::RuntimeProfileWalletLedgerSourceType::DailyTaskReward } } } pub(crate) fn map_analytics_granularity( granularity: module_runtime::AnalyticsGranularity, ) -> AnalyticsGranularity { match granularity { module_runtime::AnalyticsGranularity::Day => AnalyticsGranularity::Day, module_runtime::AnalyticsGranularity::Week => AnalyticsGranularity::Week, module_runtime::AnalyticsGranularity::Month => AnalyticsGranularity::Month, module_runtime::AnalyticsGranularity::Quarter => AnalyticsGranularity::Quarter, module_runtime::AnalyticsGranularity::Year => AnalyticsGranularity::Year, } } pub(crate) fn map_runtime_tracking_scope_kind( value: DomainRuntimeTrackingScopeKind, ) -> crate::module_bindings::RuntimeTrackingScopeKind { match value { DomainRuntimeTrackingScopeKind::Site => { crate::module_bindings::RuntimeTrackingScopeKind::Site } DomainRuntimeTrackingScopeKind::Work => { crate::module_bindings::RuntimeTrackingScopeKind::Work } DomainRuntimeTrackingScopeKind::Module => { crate::module_bindings::RuntimeTrackingScopeKind::Module } DomainRuntimeTrackingScopeKind::User => { crate::module_bindings::RuntimeTrackingScopeKind::User } } } pub(crate) fn map_runtime_tracking_scope_kind_back( value: crate::module_bindings::RuntimeTrackingScopeKind, ) -> DomainRuntimeTrackingScopeKind { match value { crate::module_bindings::RuntimeTrackingScopeKind::Site => { DomainRuntimeTrackingScopeKind::Site } crate::module_bindings::RuntimeTrackingScopeKind::Work => { DomainRuntimeTrackingScopeKind::Work } crate::module_bindings::RuntimeTrackingScopeKind::Module => { DomainRuntimeTrackingScopeKind::Module } crate::module_bindings::RuntimeTrackingScopeKind::User => { DomainRuntimeTrackingScopeKind::User } } } pub(crate) fn map_runtime_profile_task_cycle( value: DomainRuntimeProfileTaskCycle, ) -> crate::module_bindings::RuntimeProfileTaskCycle { match value { DomainRuntimeProfileTaskCycle::Daily => { crate::module_bindings::RuntimeProfileTaskCycle::Daily } } } pub(crate) fn map_runtime_profile_task_cycle_back( value: crate::module_bindings::RuntimeProfileTaskCycle, ) -> DomainRuntimeProfileTaskCycle { match value { crate::module_bindings::RuntimeProfileTaskCycle::Daily => { DomainRuntimeProfileTaskCycle::Daily } } } pub(crate) fn map_runtime_profile_task_status_back( value: crate::module_bindings::RuntimeProfileTaskStatus, ) -> DomainRuntimeProfileTaskStatus { match value { crate::module_bindings::RuntimeProfileTaskStatus::Incomplete => { DomainRuntimeProfileTaskStatus::Incomplete } crate::module_bindings::RuntimeProfileTaskStatus::Claimable => { DomainRuntimeProfileTaskStatus::Claimable } crate::module_bindings::RuntimeProfileTaskStatus::Claimed => { DomainRuntimeProfileTaskStatus::Claimed } crate::module_bindings::RuntimeProfileTaskStatus::Disabled => { DomainRuntimeProfileTaskStatus::Disabled } } } pub(crate) fn map_runtime_profile_redeem_code_mode( value: module_runtime::RuntimeProfileRedeemCodeMode, ) -> crate::module_bindings::RuntimeProfileRedeemCodeMode { match value { module_runtime::RuntimeProfileRedeemCodeMode::Public => { crate::module_bindings::RuntimeProfileRedeemCodeMode::Public } module_runtime::RuntimeProfileRedeemCodeMode::Unique => { crate::module_bindings::RuntimeProfileRedeemCodeMode::Unique } module_runtime::RuntimeProfileRedeemCodeMode::Private => { crate::module_bindings::RuntimeProfileRedeemCodeMode::Private } } } pub(crate) fn map_runtime_profile_redeem_code_mode_back( value: crate::module_bindings::RuntimeProfileRedeemCodeMode, ) -> module_runtime::RuntimeProfileRedeemCodeMode { match value { crate::module_bindings::RuntimeProfileRedeemCodeMode::Public => { module_runtime::RuntimeProfileRedeemCodeMode::Public } crate::module_bindings::RuntimeProfileRedeemCodeMode::Unique => { module_runtime::RuntimeProfileRedeemCodeMode::Unique } crate::module_bindings::RuntimeProfileRedeemCodeMode::Private => { module_runtime::RuntimeProfileRedeemCodeMode::Private } } } pub(crate) fn map_runtime_profile_recharge_product_kind( value: module_runtime::RuntimeProfileRechargeProductKind, ) -> crate::module_bindings::RuntimeProfileRechargeProductKind { match value { module_runtime::RuntimeProfileRechargeProductKind::Points => { crate::module_bindings::RuntimeProfileRechargeProductKind::Points } module_runtime::RuntimeProfileRechargeProductKind::Membership => { crate::module_bindings::RuntimeProfileRechargeProductKind::Membership } } } pub(crate) fn map_runtime_profile_recharge_product_kind_back( value: crate::module_bindings::RuntimeProfileRechargeProductKind, ) -> module_runtime::RuntimeProfileRechargeProductKind { match value { crate::module_bindings::RuntimeProfileRechargeProductKind::Points => { module_runtime::RuntimeProfileRechargeProductKind::Points } crate::module_bindings::RuntimeProfileRechargeProductKind::Membership => { module_runtime::RuntimeProfileRechargeProductKind::Membership } } } pub(crate) fn map_runtime_profile_membership_tier( value: module_runtime::RuntimeProfileMembershipTier, ) -> crate::module_bindings::RuntimeProfileMembershipTier { match value { module_runtime::RuntimeProfileMembershipTier::Normal => { crate::module_bindings::RuntimeProfileMembershipTier::Normal } module_runtime::RuntimeProfileMembershipTier::Month => { crate::module_bindings::RuntimeProfileMembershipTier::Month } module_runtime::RuntimeProfileMembershipTier::Season => { crate::module_bindings::RuntimeProfileMembershipTier::Season } module_runtime::RuntimeProfileMembershipTier::Year => { crate::module_bindings::RuntimeProfileMembershipTier::Year } } } pub(crate) fn map_runtime_profile_membership_status_back( value: crate::module_bindings::RuntimeProfileMembershipStatus, ) -> module_runtime::RuntimeProfileMembershipStatus { match value { crate::module_bindings::RuntimeProfileMembershipStatus::Normal => { module_runtime::RuntimeProfileMembershipStatus::Normal } crate::module_bindings::RuntimeProfileMembershipStatus::Active => { module_runtime::RuntimeProfileMembershipStatus::Active } } } pub(crate) fn map_runtime_profile_membership_tier_back( value: crate::module_bindings::RuntimeProfileMembershipTier, ) -> module_runtime::RuntimeProfileMembershipTier { match value { crate::module_bindings::RuntimeProfileMembershipTier::Normal => { module_runtime::RuntimeProfileMembershipTier::Normal } crate::module_bindings::RuntimeProfileMembershipTier::Month => { module_runtime::RuntimeProfileMembershipTier::Month } crate::module_bindings::RuntimeProfileMembershipTier::Season => { module_runtime::RuntimeProfileMembershipTier::Season } crate::module_bindings::RuntimeProfileMembershipTier::Year => { module_runtime::RuntimeProfileMembershipTier::Year } } } pub(crate) fn map_runtime_profile_recharge_order_status_back( value: crate::module_bindings::RuntimeProfileRechargeOrderStatus, ) -> module_runtime::RuntimeProfileRechargeOrderStatus { match value { crate::module_bindings::RuntimeProfileRechargeOrderStatus::Pending => { module_runtime::RuntimeProfileRechargeOrderStatus::Pending } crate::module_bindings::RuntimeProfileRechargeOrderStatus::Paid => { module_runtime::RuntimeProfileRechargeOrderStatus::Paid } crate::module_bindings::RuntimeProfileRechargeOrderStatus::Failed => { module_runtime::RuntimeProfileRechargeOrderStatus::Failed } crate::module_bindings::RuntimeProfileRechargeOrderStatus::Closed => { module_runtime::RuntimeProfileRechargeOrderStatus::Closed } crate::module_bindings::RuntimeProfileRechargeOrderStatus::Refunded => { module_runtime::RuntimeProfileRechargeOrderStatus::Refunded } } } pub(crate) fn map_runtime_profile_feedback_status_back( value: crate::module_bindings::RuntimeProfileFeedbackStatus, ) -> module_runtime::RuntimeProfileFeedbackStatus { match value { crate::module_bindings::RuntimeProfileFeedbackStatus::Open => { module_runtime::RuntimeProfileFeedbackStatus::Open } } } pub(crate) fn map_story_session_status(value: StorySessionStatus) -> DomainStorySessionStatus { match value { StorySessionStatus::Active => DomainStorySessionStatus::Active, StorySessionStatus::Completed => DomainStorySessionStatus::Completed, StorySessionStatus::Archived => DomainStorySessionStatus::Archived, } } pub(crate) fn map_battle_status(value: BattleStatus) -> DomainBattleStatus { match value { BattleStatus::Ongoing => DomainBattleStatus::Ongoing, BattleStatus::Resolved => DomainBattleStatus::Resolved, BattleStatus::Aborted => DomainBattleStatus::Aborted, } } pub(crate) fn map_story_event_kind(value: StoryEventKind) -> DomainStoryEventKind { match value { StoryEventKind::SessionStarted => DomainStoryEventKind::SessionStarted, StoryEventKind::StoryContinued => DomainStoryEventKind::StoryContinued, } } pub(crate) fn map_ai_task_kind(value: DomainAiTaskKind) -> AiTaskKind { match value { DomainAiTaskKind::StoryGeneration => AiTaskKind::StoryGeneration, DomainAiTaskKind::CharacterChat => AiTaskKind::CharacterChat, DomainAiTaskKind::NpcChat => AiTaskKind::NpcChat, DomainAiTaskKind::CustomWorldGeneration => AiTaskKind::CustomWorldGeneration, DomainAiTaskKind::QuestIntent => AiTaskKind::QuestIntent, DomainAiTaskKind::RuntimeItemIntent => AiTaskKind::RuntimeItemIntent, } } pub(crate) fn map_ai_task_stage_kind(value: DomainAiTaskStageKind) -> AiTaskStageKind { match value { DomainAiTaskStageKind::PreparePrompt => AiTaskStageKind::PreparePrompt, DomainAiTaskStageKind::RequestModel => AiTaskStageKind::RequestModel, DomainAiTaskStageKind::RepairResponse => AiTaskStageKind::RepairResponse, DomainAiTaskStageKind::NormalizeResult => AiTaskStageKind::NormalizeResult, DomainAiTaskStageKind::PersistResult => AiTaskStageKind::PersistResult, } } pub(crate) fn map_ai_result_reference_kind( value: DomainAiResultReferenceKind, ) -> AiResultReferenceKind { match value { DomainAiResultReferenceKind::StorySession => AiResultReferenceKind::StorySession, DomainAiResultReferenceKind::StoryEvent => AiResultReferenceKind::StoryEvent, DomainAiResultReferenceKind::CustomWorldProfile => { AiResultReferenceKind::CustomWorldProfile } DomainAiResultReferenceKind::QuestRecord => AiResultReferenceKind::QuestRecord, DomainAiResultReferenceKind::RuntimeItemRecord => AiResultReferenceKind::RuntimeItemRecord, DomainAiResultReferenceKind::AssetObject => AiResultReferenceKind::AssetObject, } } pub(crate) fn format_ai_task_kind(value: AiTaskKind) -> &'static str { match value { AiTaskKind::StoryGeneration => "story_generation", AiTaskKind::CharacterChat => "character_chat", AiTaskKind::NpcChat => "npc_chat", AiTaskKind::CustomWorldGeneration => "custom_world_generation", AiTaskKind::QuestIntent => "quest_intent", AiTaskKind::RuntimeItemIntent => "runtime_item_intent", } } pub(crate) fn format_ai_task_status(value: AiTaskStatus) -> &'static str { match value { AiTaskStatus::Pending => "pending", AiTaskStatus::Running => "running", AiTaskStatus::Completed => "completed", AiTaskStatus::Failed => "failed", AiTaskStatus::Cancelled => "cancelled", } } pub(crate) fn format_ai_task_stage_kind(value: AiTaskStageKind) -> &'static str { match value { AiTaskStageKind::PreparePrompt => "prepare_prompt", AiTaskStageKind::RequestModel => "request_model", AiTaskStageKind::RepairResponse => "repair_response", AiTaskStageKind::NormalizeResult => "normalize_result", AiTaskStageKind::PersistResult => "persist_result", } } pub(crate) fn format_ai_task_stage_status(value: AiTaskStageStatus) -> &'static str { match value { AiTaskStageStatus::Pending => "pending", AiTaskStageStatus::Running => "running", AiTaskStageStatus::Completed => "completed", AiTaskStageStatus::Skipped => "skipped", } } pub(crate) fn format_ai_result_reference_kind(value: AiResultReferenceKind) -> &'static str { match value { AiResultReferenceKind::StorySession => "story_session", AiResultReferenceKind::StoryEvent => "story_event", AiResultReferenceKind::CustomWorldProfile => "custom_world_profile", AiResultReferenceKind::QuestRecord => "quest_record", AiResultReferenceKind::RuntimeItemRecord => "runtime_item_record", AiResultReferenceKind::AssetObject => "asset_object", } } pub(crate) fn map_combat_outcome(value: CombatOutcome) -> DomainCombatOutcome { match value { CombatOutcome::Ongoing => DomainCombatOutcome::Ongoing, CombatOutcome::Victory => DomainCombatOutcome::Victory, CombatOutcome::SparComplete => DomainCombatOutcome::SparComplete, CombatOutcome::Escaped => DomainCombatOutcome::Escaped, } } pub(crate) fn map_runtime_item_reward_item_snapshot( snapshot: DomainRuntimeItemRewardItemSnapshot, ) -> RuntimeItemRewardItemSnapshot { RuntimeItemRewardItemSnapshot { item_id: snapshot.item_id, category: snapshot.category, item_name: snapshot.item_name, description: snapshot.description, quantity: snapshot.quantity, rarity: map_runtime_item_reward_item_rarity(snapshot.rarity), tags: snapshot.tags, stackable: snapshot.stackable, stack_key: snapshot.stack_key, equipment_slot_id: snapshot .equipment_slot_id .map(map_runtime_item_equipment_slot), } } pub(crate) fn map_runtime_item_reward_item_snapshot_back( snapshot: RuntimeItemRewardItemSnapshot, ) -> DomainRuntimeItemRewardItemSnapshot { DomainRuntimeItemRewardItemSnapshot { item_id: snapshot.item_id, category: snapshot.category, item_name: snapshot.item_name, description: snapshot.description, quantity: snapshot.quantity, rarity: map_runtime_item_reward_item_rarity_back(snapshot.rarity), tags: snapshot.tags, stackable: snapshot.stackable, stack_key: snapshot.stack_key, equipment_slot_id: snapshot .equipment_slot_id .map(map_runtime_item_equipment_slot_back), } } pub(crate) fn parse_json_value( value: &str, label: &str, ) -> Result { serde_json::from_str::(value) .map_err(|error| SpacetimeClientError::Runtime(format!("{label} 非法: {error}"))) } pub(crate) fn parse_optional_json_value( value: Option<&str>, fallback: serde_json::Value, label: &str, ) -> Result { match value.map(str::trim).filter(|value| !value.is_empty()) { Some(value) => parse_json_value(value, label), None => Ok(fallback), } } pub(crate) fn parse_json_array( value: &str, label: &str, ) -> Result, SpacetimeClientError> { match parse_json_value(value, label)? { serde_json::Value::Array(entries) => Ok(entries), _ => Err(SpacetimeClientError::Runtime(format!( "{label} 必须是 JSON array" ))), } } pub(crate) fn parse_json_string_array( value: &str, label: &str, ) -> Result, SpacetimeClientError> { parse_json_array(value, label)? .into_iter() .map(|entry| match entry { serde_json::Value::String(value) => Ok(value), _ => Err(SpacetimeClientError::Runtime(format!( "{label} 必须是 string array" ))), }) .collect() } pub(crate) fn map_custom_world_checkpoint_record( value: serde_json::Value, ) -> Result { let object = value.as_object().ok_or_else(|| { SpacetimeClientError::Runtime("custom world checkpoint 必须是 JSON object".to_string()) })?; let checkpoint_id = object .get("checkpointId") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .ok_or_else(|| { SpacetimeClientError::Runtime("custom world checkpoint.checkpointId 缺失".to_string()) })?; let created_at = object .get("createdAt") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .ok_or_else(|| { SpacetimeClientError::Runtime("custom world checkpoint.createdAt 缺失".to_string()) })?; let label = object .get("label") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .ok_or_else(|| { SpacetimeClientError::Runtime("custom world checkpoint.label 缺失".to_string()) })?; Ok(CustomWorldCheckpointRecord { checkpoint_id: checkpoint_id.to_string(), created_at: created_at.to_string(), label: label.to_string(), }) } pub(crate) fn parse_supported_actions_json( value: &str, ) -> Result, SpacetimeClientError> { parse_json_array(value, "custom world agent supported_actions_json")? .into_iter() .map(|entry| { let object = entry.as_object().ok_or_else(|| { SpacetimeClientError::Runtime( "custom world supported action 必须是 JSON object".to_string(), ) })?; let action = object .get("action") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .ok_or_else(|| { SpacetimeClientError::Runtime( "custom world supported action.action 缺失".to_string(), ) })?; let enabled = object .get("enabled") .and_then(serde_json::Value::as_bool) .ok_or_else(|| { SpacetimeClientError::Runtime( "custom world supported action.enabled 缺失".to_string(), ) })?; Ok(CustomWorldSupportedActionRecord { action: action.to_string(), enabled, reason: object .get("reason") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned), }) }) .collect() } pub(crate) fn parse_custom_world_publish_gate_record( value: &str, ) -> Result { let object = parse_json_value(value, "custom world publish_gate_json")? .as_object() .cloned() .ok_or_else(|| { SpacetimeClientError::Runtime( "custom world publish_gate_json 必须是 JSON object".to_string(), ) })?; let profile_id = object .get("profileId") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .ok_or_else(|| { SpacetimeClientError::Runtime("custom world publish_gate.profileId 缺失".to_string()) })?; let blockers = object .get("blockers") .and_then(serde_json::Value::as_array) .ok_or_else(|| { SpacetimeClientError::Runtime("custom world publish_gate.blockers 缺失".to_string()) })? .iter() .cloned() .map(|entry| { let object = entry.as_object().ok_or_else(|| { SpacetimeClientError::Runtime( "custom world publish gate blocker 必须是 JSON object".to_string(), ) })?; let id = object .get("id") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .ok_or_else(|| { SpacetimeClientError::Runtime( "custom world publish gate blocker.id 缺失".to_string(), ) })?; let code = object .get("code") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .ok_or_else(|| { SpacetimeClientError::Runtime( "custom world publish gate blocker.code 缺失".to_string(), ) })?; let message = object .get("message") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .ok_or_else(|| { SpacetimeClientError::Runtime( "custom world publish gate blocker.message 缺失".to_string(), ) })?; Ok(CustomWorldResultPreviewBlockerRecord { id: id.to_string(), code: code.to_string(), message: message.to_string(), }) }) .collect::, _>>()?; let blocker_count = object .get("blockerCount") .and_then(serde_json::Value::as_u64) .and_then(|value| u32::try_from(value).ok()) .ok_or_else(|| { SpacetimeClientError::Runtime("custom world publish_gate.blockerCount 缺失".to_string()) })?; let publish_ready = object .get("publishReady") .and_then(serde_json::Value::as_bool) .ok_or_else(|| { SpacetimeClientError::Runtime("custom world publish_gate.publishReady 缺失".to_string()) })?; let can_enter_world = object .get("canEnterWorld") .and_then(serde_json::Value::as_bool) .ok_or_else(|| { SpacetimeClientError::Runtime( "custom world publish_gate.canEnterWorld 缺失".to_string(), ) })?; Ok(CustomWorldPublishGateRecord { profile_id: profile_id.to_string(), blockers, blocker_count, publish_ready, can_enter_world, }) } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BattleStateRecord { pub battle_state_id: String, pub story_session_id: String, pub runtime_session_id: String, pub actor_user_id: String, pub chapter_id: Option, pub target_npc_id: String, pub target_name: String, pub battle_mode: String, pub status: String, pub player_hp: i32, pub player_max_hp: i32, pub player_mana: i32, pub player_max_mana: i32, pub target_hp: i32, pub target_max_hp: i32, pub experience_reward: u32, pub reward_items: Vec, pub turn_index: u32, pub last_action_function_id: Option, pub last_action_text: Option, pub last_result_text: Option, pub last_damage_dealt: i32, pub last_damage_taken: i32, pub last_outcome: String, pub version: u32, pub created_at: String, pub updated_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct ResolveCombatActionRecord { pub battle_state: BattleStateRecord, pub damage_dealt: i32, pub damage_taken: i32, pub outcome: String, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldLibraryEntryRecord { pub owner_user_id: String, pub profile_id: String, pub public_work_code: Option, pub author_public_user_code: Option, pub profile: serde_json::Value, pub visibility: String, pub published_at: Option, pub updated_at: String, pub author_display_name: String, pub world_name: String, pub subtitle: String, pub summary_text: String, pub cover_image_src: Option, pub theme_mode: String, pub playable_npc_count: u32, pub landmark_count: u32, pub play_count: u32, pub remix_count: u32, pub like_count: u32, pub recent_play_count_7d: u32, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldGalleryEntryRecord { pub owner_user_id: String, pub profile_id: String, pub public_work_code: String, pub author_public_user_code: String, pub visibility: String, pub published_at: Option, pub updated_at: String, pub author_display_name: String, pub world_name: String, pub subtitle: String, pub summary_text: String, pub cover_image_src: Option, pub theme_mode: String, pub playable_npc_count: u32, pub landmark_count: u32, pub play_count: u32, pub remix_count: u32, pub like_count: u32, pub recent_play_count_7d: u32, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldLibraryMutationRecord { pub entry: CustomWorldLibraryEntryRecord, pub gallery_entry: Option, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldPublishedProfileCompileRecord { pub profile_id: String, pub owner_user_id: String, pub world_name: String, pub subtitle: String, pub summary_text: String, pub theme_mode: String, pub cover_image_src: Option, pub playable_npc_count: u32, pub landmark_count: u32, pub author_display_name: String, pub compiled_profile: serde_json::Value, pub updated_at: String, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldPublishWorldRecord { pub compiled_record: CustomWorldPublishedProfileCompileRecord, pub entry: CustomWorldLibraryEntryRecord, pub gallery_entry: Option, pub session_stage: String, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldAgentMessageRecord { pub message_id: String, pub role: String, pub kind: String, pub text: String, pub created_at: String, pub related_operation_id: Option, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldAgentOperationRecord { pub operation_id: String, pub operation_type: String, pub status: String, pub phase_label: String, pub phase_detail: String, pub progress: u32, pub error_message: Option, pub started_at_micros: i64, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldAgentOperationProgressRecordInput { pub session_id: String, pub owner_user_id: String, pub operation_id: String, // SpacetimeDB 模块侧使用枚举存储操作类型,这里保留字符串给 API 层做轻量传参。 pub operation_type: String, pub operation_status: String, pub phase_label: String, pub phase_detail: String, pub operation_progress: u32, pub error_message: Option, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldDraftCardRecord { pub card_id: String, pub kind: String, pub title: String, pub subtitle: String, pub summary: String, pub status: String, pub linked_ids: Vec, pub warning_count: u32, pub asset_status: Option, pub asset_status_label: Option, pub detail_payload: Option, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldSupportedActionRecord { pub action: String, pub enabled: bool, pub reason: Option, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldCheckpointRecord { pub checkpoint_id: String, pub created_at: String, pub label: String, } // 兼容并行 custom world facade 中仍在使用的旧命名,避免本轮 module-npc 收口被无关改动阻塞。 pub type CustomWorldAgentCheckpointRecord = CustomWorldCheckpointRecord; #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldResultPreviewBlockerRecord { pub id: String, pub code: String, pub message: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldPublishGateRecord { pub profile_id: String, pub blockers: Vec, pub blocker_count: u32, pub publish_ready: bool, pub can_enter_world: bool, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldWorkSummaryRecord { pub work_id: String, pub source_type: String, pub status: String, pub title: String, pub subtitle: String, pub summary: String, pub cover_image_src: Option, pub cover_render_mode: Option, pub cover_character_image_srcs: Vec, pub updated_at: String, pub published_at: Option, pub stage: Option, pub stage_label: Option, pub playable_npc_count: u32, pub landmark_count: u32, pub role_visual_ready_count: Option, pub role_animation_ready_count: Option, pub role_asset_summary_label: Option, pub session_id: Option, pub profile_id: Option, pub can_resume: bool, pub can_enter_world: bool, pub blocker_count: u32, pub publish_ready: bool, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldDraftCardDetailSectionRecord { pub section_id: String, pub label: String, pub value: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldDraftCardDetailRecord { pub card_id: String, pub kind: String, pub title: String, pub sections: Vec, pub linked_ids: Vec, pub locked: bool, pub editable: bool, pub editable_section_ids: Vec, pub warning_messages: Vec, pub asset_status: Option, pub asset_status_label: Option, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldAgentSessionRecord { pub session_id: String, pub seed_text: String, pub current_turn: u32, pub anchor_content: serde_json::Value, pub progress_percent: u32, pub last_assistant_reply: Option, pub stage: String, pub focus_card_id: Option, pub creator_intent: serde_json::Value, pub creator_intent_readiness: serde_json::Value, pub anchor_pack: serde_json::Value, pub lock_state: serde_json::Value, pub draft_profile: serde_json::Value, pub messages: Vec, pub draft_cards: Vec, pub pending_clarifications: Vec, pub suggested_actions: Vec, pub recommended_replies: Vec, pub quality_findings: Vec, pub asset_coverage: serde_json::Value, pub checkpoints: Vec, pub supported_actions: Vec, pub publish_gate: Option, pub result_preview: Option, pub updated_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldProfileUpsertRecordInput { pub profile_id: String, pub owner_user_id: String, pub public_work_code: Option, pub author_public_user_code: Option, pub source_agent_session_id: Option, pub world_name: String, pub subtitle: String, pub summary_text: String, pub theme_mode: DomainCustomWorldThemeMode, pub cover_image_src: Option, pub profile_payload_json: String, pub playable_npc_count: u32, pub landmark_count: u32, pub author_display_name: String, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldProfileRemixRecordInput { pub source_owner_user_id: String, pub source_profile_id: String, pub target_owner_user_id: String, pub target_profile_id: String, pub author_display_name: String, pub remixed_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldProfilePlayReportRecordInput { pub owner_user_id: String, pub profile_id: String, pub played_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldProfileLikeReportRecordInput { pub owner_user_id: String, pub profile_id: String, pub user_id: String, pub liked_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldPublishWorldRecordInput { pub session_id: String, pub profile_id: String, pub owner_user_id: String, pub public_work_code: Option, pub author_public_user_code: String, pub draft_profile_json: String, pub legacy_result_profile_json: Option, pub setting_text: String, pub author_display_name: String, pub published_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldAgentSessionCreateRecordInput { pub session_id: String, pub owner_user_id: String, pub seed_text: String, pub welcome_message_id: String, pub welcome_message_text: String, pub anchor_content_json: String, pub creator_intent_json: Option, pub creator_intent_readiness_json: String, pub anchor_pack_json: Option, pub lock_state_json: Option, pub draft_profile_json: Option, pub pending_clarifications_json: String, pub suggested_actions_json: String, pub recommended_replies_json: String, pub quality_findings_json: String, pub asset_coverage_json: String, pub checkpoints_json: String, pub created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldAgentMessageSubmitRecordInput { pub session_id: String, pub owner_user_id: String, pub user_message_id: String, pub user_message_text: String, pub operation_id: String, pub submitted_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldAgentMessageFinalizeRecordInput { pub session_id: String, pub owner_user_id: String, pub operation_id: String, pub assistant_message_id: Option, pub assistant_reply_text: Option, pub phase_label: String, pub phase_detail: String, pub operation_status: String, pub operation_progress: u32, pub stage: String, pub progress_percent: u32, pub focus_card_id: Option, pub anchor_content_json: String, pub creator_intent_json: Option, pub creator_intent_readiness_json: String, pub anchor_pack_json: Option, pub draft_profile_json: Option, pub pending_clarifications_json: String, pub suggested_actions_json: String, pub recommended_replies_json: String, pub quality_findings_json: String, pub asset_coverage_json: String, pub error_message: Option, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct CustomWorldAgentActionExecuteRecordInput { pub session_id: String, pub owner_user_id: String, pub operation_id: String, pub action: String, pub payload_json: Option, pub submitted_at_micros: i64, } #[derive(Clone, Debug, PartialEq)] pub struct CustomWorldAgentActionExecuteRecord { pub operation: CustomWorldAgentOperationRecord, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleAgentSessionCreateRecordInput { pub session_id: String, pub owner_user_id: String, pub seed_text: String, pub welcome_message_id: String, pub welcome_message_text: String, pub created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleFormDraftSaveRecordInput { pub session_id: String, pub owner_user_id: String, pub seed_text: String, pub saved_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleAgentMessageSubmitRecordInput { pub session_id: String, pub owner_user_id: String, pub user_message_id: String, pub user_message_text: String, pub submitted_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleAgentMessageFinalizeRecordInput { pub session_id: String, pub owner_user_id: String, pub assistant_message_id: Option, pub assistant_reply_text: Option, pub stage: String, pub progress_percent: u32, pub anchor_pack_json: String, pub error_message: Option, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleGeneratedImagesSaveRecordInput { pub session_id: String, pub owner_user_id: String, pub level_id: Option, pub levels_json: Option, pub candidates_json: String, pub saved_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleUiBackgroundSaveRecordInput { pub session_id: String, pub owner_user_id: String, pub level_id: Option, pub levels_json: Option, pub prompt: String, pub image_src: String, pub image_object_key: Option, pub saved_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleSelectCoverImageRecordInput { pub session_id: String, pub owner_user_id: String, pub level_id: Option, pub candidate_id: String, pub selected_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzlePublishRecordInput { pub session_id: String, pub owner_user_id: String, pub work_id: String, pub profile_id: String, pub author_display_name: String, pub work_title: Option, pub work_description: Option, pub level_name: Option, pub summary: Option, pub theme_tags: Option>, pub levels_json: Option, pub published_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleWorkUpsertRecordInput { pub profile_id: String, pub owner_user_id: String, pub work_title: String, pub work_description: String, pub level_name: String, pub summary: String, pub theme_tags: Vec, pub cover_image_src: Option, pub cover_asset_id: Option, pub levels_json: Option, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleWorkRemixRecordInput { pub source_profile_id: String, pub target_owner_user_id: String, pub target_session_id: String, pub target_profile_id: String, pub target_work_id: String, pub author_display_name: String, pub welcome_message_id: String, pub remixed_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleWorkLikeReportRecordInput { pub profile_id: String, pub user_id: String, pub liked_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleRunStartRecordInput { pub run_id: String, pub owner_user_id: String, pub profile_id: String, pub level_id: Option, pub started_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleRunSwapRecordInput { pub run_id: String, pub owner_user_id: String, pub first_piece_id: String, pub second_piece_id: String, pub swapped_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleRunDragRecordInput { pub run_id: String, pub owner_user_id: String, pub piece_id: String, pub target_row: u32, pub target_col: u32, pub dragged_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleRunNextLevelRecordInput { pub run_id: String, pub owner_user_id: String, pub target_profile_id: Option, pub advanced_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleRunPauseRecordInput { pub run_id: String, pub owner_user_id: String, pub paused: bool, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleRunPropRecordInput { pub run_id: String, pub owner_user_id: String, pub prop_kind: String, pub used_at_micros: i64, pub spent_points: u64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishPlayReportRecordInput { pub session_id: String, pub user_id: String, pub elapsed_ms: u64, pub reported_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishRunStartRecordInput { pub run_id: String, pub session_id: String, pub owner_user_id: String, pub started_at_micros: i64, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishInputSubmitRecordInput { pub run_id: String, pub owner_user_id: String, pub x: f32, pub y: f32, pub submitted_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishLikeReportRecordInput { pub session_id: String, pub user_id: String, pub liked_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishWorkRemixRecordInput { pub source_session_id: String, pub target_session_id: String, pub target_owner_user_id: String, pub welcome_message_id: String, pub remixed_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DAgentSessionCreateRecordInput { pub session_id: String, pub owner_user_id: String, pub seed_text: String, pub welcome_message_id: String, pub welcome_message_text: String, pub config_json: Option, pub created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DAgentMessageSubmitRecordInput { pub session_id: String, pub owner_user_id: String, pub user_message_id: String, pub user_message_text: String, pub submitted_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DAgentMessageFinalizeRecordInput { pub session_id: String, pub owner_user_id: String, pub assistant_message_id: Option, pub assistant_reply_text: Option, pub config_json: Option, pub progress_percent: u32, pub stage: String, pub updated_at_micros: i64, pub error_message: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DCompileDraftRecordInput { pub session_id: String, pub owner_user_id: String, pub profile_id: String, pub author_display_name: String, pub game_name: Option, pub summary_text: Option, pub tags_json: Option, pub cover_image_src: Option, pub cover_asset_id: Option, pub compiled_at_micros: i64, pub generated_item_assets_json: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DWorkUpdateRecordInput { pub profile_id: String, pub owner_user_id: String, pub game_name: String, pub theme_text: String, pub summary_text: String, pub tags_json: String, pub cover_image_src: String, pub cover_asset_id: String, pub clear_count: u32, pub difficulty: u32, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DRunStartRecordInput { pub run_id: String, pub owner_user_id: String, pub profile_id: String, pub started_at_ms: i64, pub item_type_count_override: u32, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DRunClickRecordInput { pub run_id: String, pub owner_user_id: String, pub item_instance_id: String, pub client_snapshot_version: u32, pub client_event_id: String, pub clicked_at_ms: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DRunStopRecordInput { pub run_id: String, pub owner_user_id: String, pub stopped_at_ms: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DRunRestartRecordInput { pub source_run_id: String, pub next_run_id: String, pub owner_user_id: String, pub restarted_at_ms: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DRunTimeUpRecordInput { pub run_id: String, pub owner_user_id: String, pub finished_at_ms: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DAnchorItemRecord { pub key: String, pub label: String, pub value: String, pub status: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DAnchorPackRecord { pub theme: Match3DAnchorItemRecord, pub clear_count: Match3DAnchorItemRecord, pub difficulty: Match3DAnchorItemRecord, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DCreatorConfigRecord { pub theme_text: String, pub reference_image_src: Option, pub clear_count: u32, pub difficulty: u32, pub asset_style_id: Option, pub asset_style_label: Option, pub asset_style_prompt: Option, pub generate_click_sound: bool, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DResultDraftRecord { pub profile_id: String, pub game_name: String, pub theme_text: String, pub summary_text: String, pub tags: Vec, pub cover_image_src: Option, pub reference_image_src: Option, pub clear_count: u32, pub difficulty: u32, pub total_item_count: u32, pub publish_ready: bool, pub blockers: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DAgentMessageRecord { pub message_id: String, pub role: String, pub kind: String, pub text: String, pub created_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DAgentSessionRecord { pub session_id: String, pub current_turn: u32, pub progress_percent: u32, pub stage: String, pub anchor_pack: Match3DAnchorPackRecord, pub config: Option, pub draft: Option, pub messages: Vec, pub last_assistant_reply: Option, pub published_profile_id: Option, pub updated_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DWorkProfileRecord { pub work_id: String, pub profile_id: String, pub owner_user_id: String, pub source_session_id: Option, pub author_display_name: String, pub game_name: String, pub theme_text: String, pub summary: String, pub tags: Vec, pub cover_image_src: Option, pub cover_asset_id: Option, pub reference_image_src: Option, pub clear_count: u32, pub difficulty: u32, pub publication_status: String, pub play_count: u32, pub updated_at: String, pub published_at: Option, pub publish_ready: bool, pub generated_item_assets_json: Option, } #[derive(Clone, Debug, PartialEq)] pub struct Match3DItemSnapshotRecord { pub item_instance_id: String, pub item_type_id: String, pub visual_key: String, pub x: f32, pub y: f32, pub radius: f32, pub layer: u32, pub state: String, pub clickable: bool, pub tray_slot_index: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match3DTraySlotRecord { pub slot_index: u32, pub item_instance_id: Option, pub item_type_id: Option, pub visual_key: Option, } #[derive(Clone, Debug, PartialEq)] pub struct Match3DRunRecord { pub run_id: String, pub profile_id: String, pub owner_user_id: String, pub status: String, pub snapshot_version: u64, pub started_at_ms: u64, pub duration_limit_ms: u64, pub server_now_ms: Option, pub remaining_ms: u64, pub clear_count: u32, pub total_item_count: u32, pub cleared_item_count: u32, pub items: Vec, pub tray_slots: Vec, pub failure_reason: Option, pub last_confirmed_action_id: Option, } #[derive(Clone, Debug, PartialEq)] pub struct Match3DClickConfirmationRecord { pub status: String, pub accepted: bool, pub reject_reason: Option, pub accepted_item_instance_id: Option, pub entered_slot_index: Option, pub cleared_item_instance_ids: Vec, pub failure_reason: Option, pub run: Match3DRunRecord, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct Match3DCreatorConfigJsonRecord { theme_text: String, reference_image_src: Option, clear_count: u32, difficulty: u32, #[serde(default)] asset_style_id: Option, #[serde(default)] asset_style_label: Option, #[serde(default)] asset_style_prompt: Option, #[serde(default)] generate_click_sound: bool, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct Match3DAgentMessageJsonRecord { message_id: String, #[allow(dead_code)] session_id: String, role: String, kind: String, text: String, created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct Match3DDraftJsonRecord { profile_id: String, game_name: String, theme_text: String, summary_text: String, tags: Vec, clear_count: u32, difficulty: u32, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct Match3DAgentSessionJsonRecord { session_id: String, #[allow(dead_code)] owner_user_id: String, #[allow(dead_code)] seed_text: String, current_turn: u32, progress_percent: u32, stage: String, config: Match3DCreatorConfigJsonRecord, draft: Option, messages: Vec, last_assistant_reply: String, published_profile_id: Option, #[allow(dead_code)] created_at_micros: i64, updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct Match3DWorkJsonRecord { profile_id: String, owner_user_id: String, source_session_id: String, author_display_name: String, game_name: String, theme_text: String, summary_text: String, tags: Vec, cover_image_src: String, cover_asset_id: String, clear_count: u32, difficulty: u32, config: Match3DCreatorConfigJsonRecord, publication_status: String, publish_ready: bool, play_count: u32, updated_at_micros: i64, published_at_micros: Option, #[serde(default)] generated_item_assets_json: Option, } #[derive(Clone, Debug, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct Match3DItemJsonRecord { item_instance_id: String, item_type_id: String, visual_key: String, x: f32, y: f32, radius: f32, layer: u32, state: String, clickable: bool, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct Match3DTraySlotJsonRecord { slot_index: u32, item_instance_id: Option, item_type_id: Option, visual_key: Option, } #[derive(Clone, Debug, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct Match3DRunJsonRecord { run_id: String, profile_id: String, status: String, snapshot_version: u32, started_at_ms: i64, duration_limit_ms: i64, server_now_ms: i64, remaining_ms: i64, clear_count: u32, total_item_count: u32, cleared_item_count: u32, tray_slots: Vec, items: Vec, failure_reason: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleAgentSessionCreateRecordInput { pub session_id: String, pub owner_user_id: String, pub seed_text: String, pub welcome_message_id: String, pub welcome_message_text: String, pub config_json: Option, pub created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleAgentMessageSubmitRecordInput { pub session_id: String, pub owner_user_id: String, pub user_message_id: String, pub user_message_text: String, pub submitted_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleAgentMessageFinalizeRecordInput { pub session_id: String, pub owner_user_id: String, pub assistant_message_id: Option, pub assistant_reply_text: Option, pub config_json: Option, pub progress_percent: u32, pub stage: String, pub updated_at_micros: i64, pub error_message: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleCompileDraftRecordInput { pub session_id: String, pub owner_user_id: String, pub profile_id: String, pub author_display_name: String, pub game_name: Option, pub summary_text: Option, pub tags_json: Option, pub cover_image_src: Option, pub compiled_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleWorkUpdateRecordInput { pub profile_id: String, pub owner_user_id: String, pub game_name: String, pub theme_text: String, pub twist_rule: String, pub summary_text: String, pub tags_json: String, pub cover_image_src: String, pub background_prompt: String, pub background_image_src: String, pub shape_options_json: String, pub hole_options_json: String, pub shape_count: u32, pub difficulty: u32, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleRunStartRecordInput { pub run_id: String, pub owner_user_id: String, pub profile_id: String, pub started_at_ms: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleRunDropRecordInput { pub run_id: String, pub owner_user_id: String, pub hole_id: String, pub client_snapshot_version: u64, pub client_event_id: String, pub dropped_at_ms: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleRunStopRecordInput { pub run_id: String, pub owner_user_id: String, pub stopped_at_ms: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleRunRestartRecordInput { pub source_run_id: String, pub next_run_id: String, pub owner_user_id: String, pub restarted_at_ms: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleRunTimeUpRecordInput { pub run_id: String, pub owner_user_id: String, pub finished_at_ms: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct VisualNovelAgentSessionCreateRecordInput { pub session_id: String, pub owner_user_id: String, pub source_mode: String, pub seed_text: String, pub source_asset_ids_json: String, pub welcome_message_id: String, pub welcome_message_text: String, pub draft_json: Option, pub created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct VisualNovelAgentMessageSubmitRecordInput { pub session_id: String, pub owner_user_id: String, pub user_message_id: String, pub user_message_text: String, pub submitted_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct VisualNovelAgentMessageFinalizeRecordInput { pub session_id: String, pub owner_user_id: String, pub assistant_message_id: Option, pub assistant_reply_text: Option, pub draft_json: Option, pub pending_action_json: Option, pub status: String, pub progress_percent: u32, pub updated_at_micros: i64, pub error_message: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct VisualNovelWorkCompileRecordInput { pub session_id: String, pub owner_user_id: String, pub profile_id: String, pub work_id: Option, pub author_display_name: String, pub work_title: Option, pub work_description: Option, pub tags_json: Option, pub cover_image_src: Option, pub compiled_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct VisualNovelWorkUpdateRecordInput { pub profile_id: String, pub owner_user_id: String, pub work_title: String, pub work_description: String, pub tags_json: String, pub cover_image_src: Option, pub source_asset_ids_json: String, pub draft_json: String, pub publish_ready: bool, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct VisualNovelRunStartRecordInput { pub run_id: String, pub owner_user_id: String, pub profile_id: String, pub mode: String, pub snapshot_json: Option, pub started_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct VisualNovelRunSnapshotRecordInput { pub run_id: String, pub owner_user_id: String, pub status: String, pub current_scene_id: Option, pub current_phase_id: Option, pub visible_character_ids_json: String, pub flags_json: String, pub metrics_json: String, pub available_choices_json: String, pub text_mode_enabled: bool, pub snapshot_json: Option, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct VisualNovelHistoryEntryRecordInput { pub entry_id: String, pub run_id: String, pub owner_user_id: String, pub turn_index: u32, pub source: String, pub action_text: Option, pub steps_json: String, pub snapshot_before_hash: Option, pub snapshot_after_hash: Option, pub created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct VisualNovelRuntimeEventRecordInput { pub event_id: String, pub run_id: String, pub owner_user_id: String, pub profile_id: Option, pub event_kind: String, pub client_event_id: Option, pub history_entry_id: Option, pub payload_json: String, pub occurred_at_micros: i64, } #[derive(Clone, Debug, PartialEq)] pub struct VisualNovelAgentMessageRecord { pub message_id: String, pub session_id: String, pub role: String, pub kind: String, pub text: String, pub created_at: String, } #[derive(Clone, Debug, PartialEq)] pub struct VisualNovelAgentSessionRecord { pub session_id: String, pub owner_user_id: String, pub source_mode: String, pub status: String, pub seed_text: String, pub source_asset_ids: Vec, pub current_turn: u32, pub progress_percent: u32, pub messages: Vec, pub draft: Option, pub pending_action: Option, pub last_assistant_reply: Option, pub published_profile_id: Option, pub created_at: String, pub updated_at: String, } #[derive(Clone, Debug, PartialEq)] pub struct VisualNovelWorkProfileRecord { pub work_id: String, pub profile_id: String, pub owner_user_id: String, pub source_session_id: Option, pub author_display_name: String, pub work_title: String, pub work_description: String, pub tags: Vec, pub cover_image_src: Option, pub source_asset_ids: Vec, pub draft: serde_json::Value, pub publication_status: String, pub publish_ready: bool, pub play_count: u32, pub created_at: String, pub updated_at: String, pub published_at: Option, } #[derive(Clone, Debug, PartialEq)] pub struct VisualNovelHistoryEntryRecord { pub entry_id: String, pub run_id: String, pub owner_user_id: String, pub profile_id: String, pub turn_index: u32, pub source: String, pub action_text: Option, pub steps: serde_json::Value, pub snapshot_before_hash: Option, pub snapshot_after_hash: Option, pub created_at: String, } #[derive(Clone, Debug, PartialEq)] pub struct VisualNovelRunRecord { pub run_id: String, pub owner_user_id: String, pub profile_id: String, pub mode: String, pub status: String, pub current_scene_id: Option, pub current_phase_id: Option, pub visible_character_ids: Vec, pub flags: serde_json::Value, pub metrics: serde_json::Value, pub history: Vec, pub available_choices: serde_json::Value, pub text_mode_enabled: bool, pub created_at: String, pub updated_at: String, } #[derive(Clone, Debug, PartialEq)] pub struct VisualNovelRuntimeEventRecord { pub event_id: String, pub run_id: Option, pub owner_user_id: String, pub profile_id: Option, pub event_kind: String, pub client_event_id: Option, pub history_entry_id: Option, pub payload: serde_json::Value, pub occurred_at: String, } #[derive(Clone, Debug, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct VisualNovelAgentMessageJsonRecord { message_id: String, session_id: String, role: String, kind: String, text: String, created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct VisualNovelAgentSessionJsonRecord { session_id: String, owner_user_id: String, source_mode: String, status: String, seed_text: String, source_asset_ids: Vec, current_turn: u32, progress_percent: u32, messages: Vec, draft: Option, pending_action: Option, last_assistant_reply: Option, published_profile_id: Option, created_at_micros: i64, updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct VisualNovelWorkJsonRecord { work_id: String, profile_id: String, owner_user_id: String, source_session_id: Option, author_display_name: String, work_title: String, work_description: String, tags: Vec, cover_image_src: Option, source_asset_ids: Vec, draft: serde_json::Value, publication_status: String, publish_ready: bool, play_count: u32, created_at_micros: i64, updated_at_micros: i64, published_at_micros: Option, } #[derive(Clone, Debug, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct VisualNovelHistoryEntryJsonRecord { entry_id: String, run_id: String, owner_user_id: String, profile_id: String, turn_index: u32, source: String, action_text: Option, steps: serde_json::Value, snapshot_before_hash: Option, snapshot_after_hash: Option, created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct VisualNovelRunJsonRecord { run_id: String, owner_user_id: String, profile_id: String, mode: String, status: String, current_scene_id: Option, current_phase_id: Option, visible_character_ids: Vec, flags: serde_json::Value, metrics: serde_json::Value, history: Vec, available_choices: serde_json::Value, text_mode_enabled: bool, created_at_micros: i64, updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct VisualNovelRuntimeEventJsonRecord { event_id: String, run_id: Option, owner_user_id: String, profile_id: Option, event_kind: String, client_event_id: Option, history_entry_id: Option, payload: serde_json::Value, occurred_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleAnchorItemRecord { pub key: String, pub label: String, pub value: String, pub status: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleAnchorPackRecord { pub theme: SquareHoleAnchorItemRecord, pub twist_rule: SquareHoleAnchorItemRecord, pub shape_count: SquareHoleAnchorItemRecord, pub difficulty: SquareHoleAnchorItemRecord, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleCreatorConfigRecord { pub theme_text: String, pub twist_rule: String, pub shape_count: u32, pub difficulty: u32, pub shape_options: Vec, pub hole_options: Vec, pub background_prompt: String, pub cover_image_src: Option, pub background_image_src: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleShapeOptionRecord { pub option_id: String, pub shape_kind: String, pub label: String, pub target_hole_id: String, pub image_prompt: String, pub image_src: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleHoleOptionRecord { pub hole_id: String, pub hole_kind: String, pub label: String, pub image_prompt: String, pub image_src: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleResultDraftRecord { pub profile_id: String, pub game_name: String, pub theme_text: String, pub twist_rule: String, pub summary: String, pub tags: Vec, pub cover_image_src: Option, pub background_prompt: String, pub background_image_src: Option, pub shape_options: Vec, pub hole_options: Vec, pub shape_count: u32, pub difficulty: u32, pub publish_ready: bool, pub blockers: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleAgentMessageRecord { pub id: String, pub role: String, pub kind: String, pub text: String, pub created_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleAgentSessionRecord { pub session_id: String, pub current_turn: u32, pub progress_percent: u32, pub stage: String, pub anchor_pack: SquareHoleAnchorPackRecord, pub config: SquareHoleCreatorConfigRecord, pub draft: Option, pub messages: Vec, pub last_assistant_reply: Option, pub published_profile_id: Option, pub updated_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleWorkProfileRecord { pub work_id: String, pub profile_id: String, pub owner_user_id: String, pub source_session_id: Option, pub author_display_name: String, pub game_name: String, pub theme_text: String, pub twist_rule: String, pub summary: String, pub tags: Vec, pub cover_image_src: Option, pub background_prompt: String, pub background_image_src: Option, pub shape_options: Vec, pub hole_options: Vec, pub shape_count: u32, pub difficulty: u32, pub publication_status: String, pub play_count: u32, pub updated_at: String, pub published_at: Option, pub publish_ready: bool, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleShapeSnapshotRecord { pub shape_id: String, pub shape_kind: String, pub label: String, pub target_hole_id: String, pub color: String, pub image_src: Option, } #[derive(Clone, Debug, PartialEq)] pub struct SquareHoleHoleSnapshotRecord { pub hole_id: String, pub hole_kind: String, pub label: String, pub x: f32, pub y: f32, pub image_src: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SquareHoleDropFeedbackRecord { pub accepted: bool, pub reject_reason: Option, pub message: String, } #[derive(Clone, Debug, PartialEq)] pub struct SquareHoleRunRecord { pub run_id: String, pub profile_id: String, pub owner_user_id: String, pub status: String, pub snapshot_version: u64, pub started_at_ms: u64, pub duration_limit_ms: u64, pub server_now_ms: Option, pub remaining_ms: u64, pub total_shape_count: u32, pub completed_shape_count: u32, pub combo: u32, pub best_combo: u32, pub score: u32, pub rule_label: String, pub background_image_src: Option, pub current_shape: Option, pub holes: Vec, pub last_feedback: Option, pub last_confirmed_action_id: Option, } #[derive(Clone, Debug, PartialEq)] pub struct SquareHoleDropConfirmationRecord { pub status: String, pub accepted: bool, pub reject_reason: Option, pub failure_reason: Option, pub feedback: SquareHoleDropFeedbackRecord, pub run: SquareHoleRunRecord, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleCreatorConfigJsonRecord { theme_text: String, twist_rule: String, shape_count: u32, difficulty: u32, #[serde(default)] shape_options: Vec, #[serde(default)] hole_options: Vec, #[serde(default)] background_prompt: String, #[serde(default)] cover_image_src: String, #[serde(default)] background_image_src: String, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleShapeOptionJsonRecord { option_id: String, shape_kind: String, label: String, #[serde(default)] target_hole_id: String, image_prompt: String, #[serde(default)] image_src: String, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleHoleOptionJsonRecord { hole_id: String, hole_kind: String, label: String, #[serde(default)] image_prompt: String, #[serde(default)] image_src: String, #[serde(default)] bonus: bool, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleAgentMessageJsonRecord { message_id: String, #[allow(dead_code)] session_id: String, role: String, kind: String, text: String, created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleDraftJsonRecord { profile_id: String, game_name: String, theme_text: String, twist_rule: String, summary_text: String, tags: Vec, #[serde(default)] cover_image_src: String, #[serde(default)] background_prompt: String, #[serde(default)] background_image_src: String, #[serde(default)] shape_options: Vec, #[serde(default)] hole_options: Vec, shape_count: u32, difficulty: u32, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleAgentSessionJsonRecord { session_id: String, #[allow(dead_code)] owner_user_id: String, #[allow(dead_code)] seed_text: String, current_turn: u32, progress_percent: u32, stage: String, config: SquareHoleCreatorConfigJsonRecord, draft: Option, messages: Vec, last_assistant_reply: String, published_profile_id: Option, #[allow(dead_code)] created_at_micros: i64, updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleWorkJsonRecord { work_id: String, profile_id: String, owner_user_id: String, source_session_id: String, author_display_name: String, game_name: String, theme_text: String, twist_rule: String, summary_text: String, tags: Vec, cover_image_src: String, #[serde(default)] background_prompt: String, #[serde(default)] background_image_src: String, #[serde(default)] shape_options: Vec, #[serde(default)] hole_options: Vec, shape_count: u32, difficulty: u32, #[allow(dead_code)] config: SquareHoleCreatorConfigJsonRecord, publication_status: String, publish_ready: bool, play_count: u32, updated_at_micros: i64, published_at_micros: Option, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleShapeJsonRecord { shape_id: String, shape_kind: String, label: String, #[serde(default)] target_hole_id: String, color: String, #[serde(default)] image_src: String, } #[derive(Clone, Debug, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleHoleJsonRecord { hole_id: String, hole_kind: String, label: String, x: f32, y: f32, #[serde(default)] image_src: String, #[serde(default)] bonus: bool, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleDropFeedbackJsonRecord { accepted: bool, reject_reason: Option, message: String, } #[derive(Clone, Debug, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct SquareHoleRunJsonRecord { run_id: String, profile_id: String, owner_user_id: String, status: String, snapshot_version: u64, started_at_ms: i64, duration_limit_ms: i64, server_now_ms: i64, remaining_ms: i64, total_shape_count: u32, completed_shape_count: u32, combo: u32, best_combo: u32, score: u32, rule_label: String, #[serde(default)] background_image_src: String, #[serde(default)] #[allow(dead_code)] shape_options: Vec, current_shape: Option, holes: Vec, last_feedback: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleAnchorItemRecord { pub key: String, pub label: String, pub value: String, pub status: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleAnchorPackRecord { pub theme_promise: PuzzleAnchorItemRecord, pub visual_subject: PuzzleAnchorItemRecord, pub visual_mood: PuzzleAnchorItemRecord, pub composition_hooks: PuzzleAnchorItemRecord, pub tags_and_forbidden: PuzzleAnchorItemRecord, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleCreatorIntentRecord { pub source_mode: String, pub raw_messages_summary: String, pub theme_promise: String, pub visual_subject: String, pub visual_mood: Vec, pub composition_hooks: Vec, pub theme_tags: Vec, pub forbidden_directives: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleGeneratedImageCandidateRecord { pub candidate_id: String, pub image_src: String, pub asset_id: String, pub prompt: String, pub actual_prompt: Option, pub source_type: String, pub selected: bool, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleResultDraftRecord { pub work_title: String, pub work_description: String, pub level_name: String, pub summary: String, pub theme_tags: Vec, pub forbidden_directives: Vec, pub creator_intent: Option, pub anchor_pack: PuzzleAnchorPackRecord, pub candidates: Vec, pub selected_candidate_id: Option, pub cover_image_src: Option, pub cover_asset_id: Option, pub generation_status: String, pub levels: Vec, pub form_draft: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleFormDraftRecord { pub work_title: Option, pub work_description: Option, pub picture_description: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleDraftLevelRecord { pub level_id: String, pub level_name: String, pub picture_description: String, pub picture_reference: Option, pub ui_background_prompt: Option, pub ui_background_image_src: Option, pub ui_background_image_object_key: Option, pub background_music: Option, pub candidates: Vec, pub selected_candidate_id: Option, pub cover_image_src: Option, pub cover_asset_id: Option, pub generation_status: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleAudioAssetRecord { pub task_id: String, pub provider: String, pub asset_object_id: Option, pub asset_kind: Option, pub audio_src: String, pub prompt: Option, pub title: Option, pub updated_at: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleAgentMessageRecord { pub message_id: String, pub role: String, pub kind: String, pub text: String, pub created_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleAgentSuggestedActionRecord { pub action_id: String, pub action_type: String, pub label: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleResultPreviewBlockerRecord { pub blocker_id: String, pub code: String, pub message: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleResultPreviewFindingRecord { pub finding_id: String, pub severity: String, pub code: String, pub message: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleResultPreviewRecord { pub draft: PuzzleResultDraftRecord, pub blockers: Vec, pub quality_findings: Vec, pub publish_ready: bool, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleAgentSessionRecord { pub session_id: String, pub seed_text: String, pub current_turn: u32, pub progress_percent: u32, pub stage: String, pub anchor_pack: PuzzleAnchorPackRecord, pub draft: Option, pub messages: Vec, pub last_assistant_reply: Option, pub published_profile_id: Option, pub suggested_actions: Vec, pub result_preview: Option, pub updated_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleWorkProfileRecord { pub work_id: String, pub profile_id: String, pub owner_user_id: String, pub source_session_id: Option, pub author_display_name: String, pub work_title: String, pub work_description: String, pub level_name: String, pub summary: String, pub theme_tags: Vec, pub cover_image_src: Option, pub cover_asset_id: Option, pub publication_status: String, pub updated_at: String, pub published_at: Option, pub play_count: u32, pub remix_count: u32, pub like_count: u32, pub recent_play_count_7d: u32, pub point_incentive_total_half_points: u64, pub point_incentive_claimed_points: u64, pub publish_ready: bool, pub anchor_pack: PuzzleAnchorPackRecord, pub levels: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleWorkPointIncentiveClaimRecordInput { pub profile_id: String, pub owner_user_id: String, pub claimed_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleCellPositionRecord { pub row: u32, pub col: u32, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzlePieceStateRecord { pub piece_id: String, pub correct_row: u32, pub correct_col: u32, pub current_row: u32, pub current_col: u32, pub merged_group_id: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleMergedGroupRecord { pub group_id: String, pub piece_ids: Vec, pub occupied_cells: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleLeaderboardEntryRecord { pub rank: u32, pub nickname: String, pub elapsed_ms: u64, pub visible_tags: Vec, pub is_current_player: bool, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleBoardRecord { pub rows: u32, pub cols: u32, pub pieces: Vec, pub merged_groups: Vec, pub selected_piece_id: Option, pub all_tiles_resolved: bool, } #[derive(Clone, Debug, PartialEq)] pub struct PuzzleRecommendedNextWorkRecord { pub profile_id: String, pub level_name: String, pub author_display_name: String, pub theme_tags: Vec, pub cover_image_src: Option, pub similarity_score: f32, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleRuntimeLevelRecord { pub run_id: String, pub level_index: u32, pub level_id: Option, pub grid_size: u32, pub profile_id: String, pub level_name: String, pub author_display_name: String, pub theme_tags: Vec, pub cover_image_src: Option, pub ui_background_image_src: Option, pub ui_background_image_object_key: Option, pub background_music: Option, pub board: PuzzleBoardRecord, pub status: String, pub started_at_ms: u64, pub cleared_at_ms: Option, pub elapsed_ms: Option, pub time_limit_ms: u64, pub remaining_ms: u64, pub paused_accumulated_ms: u64, pub pause_started_at_ms: Option, pub freeze_accumulated_ms: u64, pub freeze_started_at_ms: Option, pub freeze_until_ms: Option, pub leaderboard_entries: Vec, } #[derive(Clone, Debug, PartialEq)] pub struct PuzzleRunRecord { pub run_id: String, pub entry_profile_id: String, pub cleared_level_count: u32, pub current_level_index: u32, pub current_grid_size: u32, pub played_profile_ids: Vec, pub previous_level_tags: Vec, pub current_level: Option, pub recommended_next_profile_id: Option, pub next_level_mode: String, pub next_level_profile_id: Option, pub next_level_id: Option, pub recommended_next_works: Vec, pub leaderboard_entries: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleLeaderboardSubmitRecordInput { pub run_id: String, pub owner_user_id: String, pub profile_id: String, pub grid_size: u32, pub elapsed_ms: u64, pub nickname: String, pub submitted_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishSessionCreateRecordInput { pub session_id: String, pub owner_user_id: String, pub seed_text: String, pub welcome_message_id: String, pub welcome_message_text: String, pub created_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishMessageSubmitRecordInput { pub session_id: String, pub owner_user_id: String, pub user_message_id: String, pub user_message_text: String, pub assistant_message_id: String, pub submitted_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishMessageFinalizeRecordInput { pub session_id: String, pub owner_user_id: String, pub assistant_message_id: Option, pub assistant_reply_text: Option, pub stage: String, pub progress_percent: u32, pub anchor_pack_json: String, pub error_message: Option, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishDraftCompileRecordInput { pub session_id: String, pub owner_user_id: String, pub draft_json: Option, pub compiled_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishAssetGenerateRecordInput { pub session_id: String, pub owner_user_id: String, pub asset_kind: String, pub level: Option, pub motion_key: Option, pub asset_url: Option, pub generated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishAnchorItemRecord { pub key: String, pub label: String, pub value: String, pub status: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishAnchorPackRecord { pub gameplay_promise: BigFishAnchorItemRecord, pub ecology_visual_theme: BigFishAnchorItemRecord, pub growth_ladder: BigFishAnchorItemRecord, pub risk_tempo: BigFishAnchorItemRecord, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishLevelBlueprintRecord { pub level: u32, pub name: String, pub one_line_fantasy: String, pub text_description: String, pub silhouette_direction: String, pub size_ratio: f32, pub visual_description: String, pub visual_prompt_seed: String, pub idle_motion_description: String, pub move_motion_description: String, pub motion_prompt_seed: String, pub merge_source_level: Option, pub prey_window: Vec, pub threat_window: Vec, pub is_final_level: bool, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishBackgroundBlueprintRecord { pub theme: String, pub color_mood: String, pub foreground_hints: String, pub midground_composition: String, pub background_depth: String, pub safe_play_area_hint: String, pub spawn_edge_hint: String, pub background_prompt_seed: String, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishRuntimeParamsRecord { pub level_count: u32, pub merge_count_per_upgrade: u32, pub spawn_target_count: u32, pub leader_move_speed: f32, pub follower_catch_up_speed: f32, pub offscreen_cull_seconds: f32, pub prey_spawn_delta_levels: Vec, pub threat_spawn_delta_levels: Vec, pub win_level: u32, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishGameDraftRecord { pub title: String, pub subtitle: String, pub core_fun: String, pub ecology_theme: String, pub levels: Vec, pub background: BigFishBackgroundBlueprintRecord, pub runtime_params: BigFishRuntimeParamsRecord, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishAgentMessageRecord { pub message_id: String, pub role: String, pub kind: String, pub text: String, pub created_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishAssetSlotRecord { pub slot_id: String, pub asset_kind: String, pub level: Option, pub motion_key: Option, pub status: String, pub asset_url: Option, pub prompt_snapshot: String, pub updated_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishAssetCoverageRecord { pub level_main_image_ready_count: u32, pub level_motion_ready_count: u32, pub background_ready: bool, pub required_level_count: u32, pub publish_ready: bool, pub blockers: Vec, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishSessionRecord { pub session_id: String, pub current_turn: u32, pub progress_percent: u32, pub stage: String, pub anchor_pack: BigFishAnchorPackRecord, pub draft: Option, pub asset_slots: Vec, pub asset_coverage: BigFishAssetCoverageRecord, pub messages: Vec, pub last_assistant_reply: Option, pub publish_ready: bool, pub updated_at: String, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishVector2Record { pub x: f32, pub y: f32, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishRuntimeEntityRecord { pub entity_id: String, pub level: u32, pub position: BigFishVector2Record, pub radius: f32, pub offscreen_seconds: f32, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishRuntimeRunRecord { pub run_id: String, pub session_id: String, pub status: String, pub tick: u64, pub player_level: u32, pub win_level: u32, pub leader_entity_id: Option, pub owned_entities: Vec, pub wild_entities: Vec, pub camera_center: BigFishVector2Record, pub last_input: BigFishVector2Record, pub event_log: Vec, pub updated_at: String, } #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct BigFishWorkSummaryRecord { pub work_id: String, pub source_session_id: String, pub owner_user_id: String, pub title: String, pub subtitle: String, pub summary: String, pub cover_image_src: Option, pub status: String, pub updated_at_micros: i64, pub published_at_micros: Option, pub publish_ready: bool, pub level_count: u32, pub level_main_image_ready_count: u32, pub level_motion_ready_count: u32, pub background_ready: bool, pub play_count: u32, pub remix_count: u32, pub like_count: u32, pub recent_play_count_7d: u32, } #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] struct CompatibleBigFishWorkSummaryRecord { work_id: String, source_session_id: String, #[serde(default)] owner_user_id: Option, title: String, subtitle: String, summary: String, cover_image_src: Option, status: String, updated_at_micros: i64, #[serde(default)] published_at_micros: Option, publish_ready: bool, level_count: u32, level_main_image_ready_count: u32, level_motion_ready_count: u32, background_ready: bool, #[serde(default)] play_count: u32, #[serde(default)] remix_count: u32, #[serde(default)] like_count: u32, #[serde(default)] recent_play_count_7d: u32, } impl CompatibleBigFishWorkSummaryRecord { fn into_record(self, fallback_owner_user_id: Option<&str>) -> BigFishWorkSummaryRecord { BigFishWorkSummaryRecord { work_id: self.work_id, source_session_id: self.source_session_id, // 中文注释:兼容旧 works JSON 没有 owner_user_id 的历史数据,避免一次字段升级把整个作品列表打崩。 owner_user_id: self.owner_user_id.unwrap_or_else(|| { fallback_owner_user_id .map(str::to_string) .unwrap_or_default() }), title: self.title, subtitle: self.subtitle, summary: self.summary, cover_image_src: self.cover_image_src, status: self.status, updated_at_micros: self.updated_at_micros, published_at_micros: self.published_at_micros, publish_ready: self.publish_ready, level_count: self.level_count, level_main_image_ready_count: self.level_main_image_ready_count, level_motion_ready_count: self.level_motion_ready_count, background_ready: self.background_ready, play_count: self.play_count, remix_count: self.remix_count, like_count: self.like_count, recent_play_count_7d: self.recent_play_count_7d, } } } #[cfg(test)] mod tests { use super::*; #[test] fn puzzle_works_mapper_backfills_missing_public_stat_fields() { let result = PuzzleWorksProcedureResult { ok: true, items_json: Some( r#"[{ "work_id":"puzzle-work-1", "profile_id":"puzzle-profile-1", "owner_user_id":"user-1", "source_session_id":null, "author_display_name":"测试作者", "level_name":"雨夜拼图", "summary":"旧公开作品摘要", "theme_tags":["雨夜","猫咪","神庙"], "cover_image_src":null, "cover_asset_id":null, "publication_status":"Published", "updated_at_micros":123000000, "published_at_micros":123000000, "publish_ready":true, "anchor_pack":{ "theme_promise":{ "key":"themePromise", "label":"题材承诺", "value":"雨夜冒险", "status":"Inferred" }, "visual_subject":{ "key":"visualSubject", "label":"画面主体", "value":"猫咪神庙", "status":"Inferred" }, "visual_mood":{ "key":"visualMood", "label":"视觉气质", "value":"温暖", "status":"Inferred" }, "composition_hooks":{ "key":"compositionHooks", "label":"拼图记忆点", "value":"灯光", "status":"Inferred" }, "tags_and_forbidden":{ "key":"tagsAndForbidden", "label":"标签与禁忌", "value":"雨夜, 猫咪, 神庙", "status":"Inferred" } } }]"# .to_string(), ), error_message: None, }; let items = map_puzzle_works_procedure_result(result) .expect("旧 puzzle works JSON 缺统计字段时应按 0 兼容"); assert_eq!(items.len(), 1); assert_eq!(items[0].play_count, 0); assert_eq!(items[0].remix_count, 0); assert_eq!(items[0].like_count, 0); } #[test] fn puzzle_run_mapper_backfills_missing_timer_fields() { let result = PuzzleRunProcedureResult { ok: true, run_json: Some( r#"{ "run_id":"puzzle-run-1", "entry_profile_id":"puzzle-profile-1", "cleared_level_count":0, "current_level_index":1, "current_grid_size":3, "played_profile_ids":["puzzle-profile-1"], "previous_level_tags":["雨夜","猫咪","神庙"], "current_level":{ "run_id":"puzzle-run-1", "level_index":1, "grid_size":3, "profile_id":"puzzle-profile-1", "level_name":"雨夜拼图", "author_display_name":"测试作者", "theme_tags":["雨夜","猫咪","神庙"], "cover_image_src":null, "board":{ "rows":3, "cols":3, "pieces":[{ "piece_id":"piece-1", "correct_row":0, "correct_col":0, "current_row":0, "current_col":0, "merged_group_id":null }], "merged_groups":[], "selected_piece_id":null }, "status":"Playing" }, "recommended_next_profile_id":null }"# .to_string(), ), error_message: None, }; let run = map_puzzle_run_procedure_result(result) .expect("旧 puzzle run JSON 缺计时字段时应按默认值兼容"); let level = run.current_level.expect("兼容后仍应保留当前关卡"); assert_eq!(run.run_id, "puzzle-run-1"); assert!(level.started_at_ms > 0); assert_eq!(level.time_limit_ms, 0); assert_eq!(level.remaining_ms, 0); assert!(level.leaderboard_entries.is_empty()); } #[test] fn big_fish_works_mapper_backfills_missing_owner_user_id_for_private_lists() { let result = BigFishWorksProcedureResult { ok: true, items_json: Some( r#"[{ "work_id":"big-fish-work-session-1", "source_session_id":"session-1", "title":"深海草稿", "subtitle":"副标题", "summary":"摘要", "cover_image_src":null, "status":"draft", "updated_at_micros":123, "publish_ready":false, "level_count":8, "level_main_image_ready_count":0, "level_motion_ready_count":0, "background_ready":false }]"# .to_string(), ), error_message: None, }; let items = map_big_fish_works_procedure_result(result, Some("user-1")) .expect("旧 works JSON 应能被兼容解析"); assert_eq!(items.len(), 1); assert_eq!(items[0].owner_user_id, "user-1"); assert_eq!(items[0].published_at_micros, None); assert_eq!(items[0].play_count, 0); assert_eq!(items[0].remix_count, 0); assert_eq!(items[0].like_count, 0); } #[test] fn big_fish_works_mapper_keeps_empty_owner_when_gallery_legacy_json_lacks_field() { let result = BigFishWorksProcedureResult { ok: true, items_json: Some( r#"[{ "work_id":"big-fish-work-session-2", "source_session_id":"session-2", "title":"公开作品", "subtitle":"副标题", "summary":"摘要", "cover_image_src":null, "status":"published", "updated_at_micros":456, "publish_ready":true, "level_count":8, "level_main_image_ready_count":8, "level_motion_ready_count":16, "background_ready":true }]"# .to_string(), ), error_message: None, }; let items = map_big_fish_works_procedure_result(result, None) .expect("公开 works 旧 JSON 也不应因缺字段报错"); assert_eq!(items.len(), 1); assert!(items[0].owner_user_id.is_empty()); assert_eq!(items[0].published_at_micros, None); assert_eq!(items[0].play_count, 0); assert_eq!(items[0].remix_count, 0); assert_eq!(items[0].like_count, 0); } #[test] fn match3d_work_mapper_keeps_generated_item_assets_json() { let result = Match3DWorkProcedureResult { ok: true, work_json: Some( r#"{ "profileId":"match3d-profile-1", "ownerUserId":"user-1", "sourceSessionId":"match3d-session-1", "authorDisplayName":"测试作者", "gameName":"水果抓大鹅", "themeText":"水果", "summaryText":"水果主题", "tags":["水果"], "coverImageSrc":"", "coverAssetId":"", "clearCount":3, "difficulty":3, "config":{ "themeText":"水果", "referenceImageSrc":null, "clearCount":3, "difficulty":3 }, "publicationStatus":"Draft", "publishReady":false, "playCount":0, "updatedAtMicros":123000000, "publishedAtMicros":null, "generatedItemAssetsJson":"[{\"itemId\":\"match3d-item-1\",\"itemName\":\"草莓\",\"imageSrc\":\"/generated-match3d-assets/session/profile/items/item/image.png\",\"status\":\"image_ready\"}]" }"# .to_string(), ), error_message: None, }; let item = map_match3d_work_procedure_result(result) .expect("match3d work JSON 应保留生成素材 JSON"); assert_eq!( item.generated_item_assets_json.as_deref(), Some( r#"[{"itemId":"match3d-item-1","itemName":"草莓","imageSrc":"/generated-match3d-assets/session/profile/items/item/image.png","status":"image_ready"}]"# ) ); } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct ResolveNpcBattleInteractionInput { pub npc_interaction: DomainResolveNpcInteractionInput, pub story_session_id: String, pub actor_user_id: String, pub battle_state_id: Option, pub player_hp: i32, pub player_max_hp: i32, pub player_mana: i32, pub player_max_mana: i32, pub target_hp: i32, pub target_max_hp: i32, pub experience_reward: u32, pub reward_items: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct AiTaskStageRecord { pub stage_kind: String, pub label: String, pub detail: String, pub order: u32, pub status: String, pub text_output: Option, pub structured_payload_json: Option, pub warning_messages: Vec, pub started_at: Option, pub completed_at: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct AiResultReferenceRecord { pub result_ref_id: String, pub task_id: String, pub reference_kind: String, pub reference_id: String, pub label: Option, pub created_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct AiTextChunkRecord { pub chunk_id: String, pub task_id: String, pub stage_kind: String, pub sequence: u32, pub delta_text: String, pub created_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct AiTaskRecord { pub task_id: String, pub task_kind: String, pub owner_user_id: String, pub request_label: String, pub source_module: String, pub source_entity_id: Option, pub request_payload_json: Option, pub status: String, pub failure_message: Option, pub stages: Vec, pub result_references: Vec, pub latest_text_output: Option, pub latest_structured_payload_json: Option, pub version: u32, pub created_at: String, pub started_at: Option, pub completed_at: Option, pub updated_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct AiTaskMutationRecord { pub task: AiTaskRecord, pub text_chunk: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct NpcStateRecord { pub npc_state_id: String, pub runtime_session_id: String, pub npc_id: String, pub npc_name: String, pub affinity: i32, pub relation_stance: String, pub help_used: bool, pub chatted_count: u32, pub gifts_given: u32, pub recruited: bool, pub trade_stock_signature: Option, pub revealed_facts: Vec, pub known_attribute_rumors: Vec, pub first_meaningful_contact_resolved: bool, pub seen_backstory_chapter_ids: Vec, pub trust: u8, pub warmth: u8, pub ideological_fit: u8, pub fear_or_guard: u8, pub loyalty: u8, pub current_conflict_tag: Option, pub recent_approvals: Vec, pub recent_disapprovals: Vec, pub created_at: String, pub updated_at: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct NpcInteractionRecord { pub npc_state: NpcStateRecord, pub interaction_status: String, pub action_text: String, pub result_text: String, pub story_text: Option, pub battle_mode: Option, pub encounter_closed: bool, pub affinity_changed: bool, pub previous_affinity: i32, pub next_affinity: i32, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct NpcBattleInteractionRecord { pub npc_interaction: NpcInteractionRecord, pub battle_state: BattleStateRecord, } #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct NpcBattleInteractionSnapshot { interaction: DomainNpcInteractionResult, battle_state: DomainBattleStateSnapshot, } pub(crate) fn build_battle_state_record(snapshot: DomainBattleStateSnapshot) -> BattleStateRecord { BattleStateRecord { battle_state_id: snapshot.battle_state_id, story_session_id: snapshot.story_session_id, runtime_session_id: snapshot.runtime_session_id, actor_user_id: snapshot.actor_user_id, chapter_id: snapshot.chapter_id, target_npc_id: snapshot.target_npc_id, target_name: snapshot.target_name, battle_mode: snapshot.battle_mode.as_str().to_string(), status: snapshot.status.as_str().to_string(), player_hp: snapshot.player_hp, player_max_hp: snapshot.player_max_hp, player_mana: snapshot.player_mana, player_max_mana: snapshot.player_max_mana, target_hp: snapshot.target_hp, target_max_hp: snapshot.target_max_hp, experience_reward: snapshot.experience_reward, reward_items: snapshot.reward_items, turn_index: snapshot.turn_index, last_action_function_id: snapshot.last_action_function_id, last_action_text: snapshot.last_action_text, last_result_text: snapshot.last_result_text, last_damage_dealt: snapshot.last_damage_dealt, last_damage_taken: snapshot.last_damage_taken, last_outcome: snapshot.last_outcome.as_str().to_string(), version: snapshot.version, created_at: format_timestamp_micros(snapshot.created_at_micros), updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } pub(crate) fn build_resolve_combat_action_record( result: DomainResolveCombatActionResult, ) -> ResolveCombatActionRecord { ResolveCombatActionRecord { battle_state: build_battle_state_record(result.snapshot), damage_dealt: result.damage_dealt, damage_taken: result.damage_taken, outcome: result.outcome.as_str().to_string(), } } impl From for crate::module_bindings::ResolveNpcBattleInteractionInput { fn from(input: ResolveNpcBattleInteractionInput) -> Self { Self { npc_interaction: crate::module_bindings::ResolveNpcInteractionInput { runtime_session_id: input.npc_interaction.runtime_session_id, npc_id: input.npc_interaction.npc_id, npc_name: input.npc_interaction.npc_name, interaction_function_id: input.npc_interaction.interaction_function_id, release_npc_id: input.npc_interaction.release_npc_id, updated_at_micros: input.npc_interaction.updated_at_micros, }, story_session_id: input.story_session_id, actor_user_id: input.actor_user_id, battle_state_id: input.battle_state_id, player_hp: input.player_hp, player_max_hp: input.player_max_hp, player_mana: input.player_mana, player_max_mana: input.player_max_mana, target_hp: input.target_hp, target_max_hp: input.target_max_hp, experience_reward: input.experience_reward, reward_items: input .reward_items .into_iter() .map(map_runtime_item_reward_item_snapshot) .collect(), } } } pub(crate) fn validate_npc_battle_interaction_input( input: &ResolveNpcBattleInteractionInput, ) -> Result<(), SpacetimeClientError> { let battle_state_input = DomainBattleStateInput { battle_state_id: input .battle_state_id .clone() .unwrap_or_else(|| "battle_preview".to_string()), story_session_id: input.story_session_id.clone(), runtime_session_id: input.npc_interaction.runtime_session_id.clone(), actor_user_id: input.actor_user_id.clone(), chapter_id: None, target_npc_id: input.npc_interaction.npc_id.clone(), target_name: input.npc_interaction.npc_name.clone(), battle_mode: DomainBattleMode::Fight, player_hp: input.player_hp, player_max_hp: input.player_max_hp, player_mana: input.player_mana, player_max_mana: input.player_max_mana, target_hp: input.target_hp, target_max_hp: input.target_max_hp, experience_reward: input.experience_reward, reward_items: input.reward_items.clone(), created_at_micros: input.npc_interaction.updated_at_micros, }; validate_battle_state_input(&battle_state_input) .map_err(|error| SpacetimeClientError::Runtime(error.to_string()))?; for reward_item in input.reward_items.iter().cloned() { normalize_reward_item_snapshot(reward_item) .map_err(|error| SpacetimeClientError::Runtime(error.to_string()))?; } Ok(()) } pub(crate) fn build_npc_state_record(snapshot: DomainNpcStateSnapshot) -> NpcStateRecord { NpcStateRecord { npc_state_id: snapshot.npc_state_id, runtime_session_id: snapshot.runtime_session_id, npc_id: snapshot.npc_id, npc_name: snapshot.npc_name, affinity: snapshot.affinity, relation_stance: format_npc_relation_stance(snapshot.relation_state.stance).to_string(), help_used: snapshot.help_used, chatted_count: snapshot.chatted_count, gifts_given: snapshot.gifts_given, recruited: snapshot.recruited, trade_stock_signature: snapshot.trade_stock_signature, revealed_facts: snapshot.revealed_facts, known_attribute_rumors: snapshot.known_attribute_rumors, first_meaningful_contact_resolved: snapshot.first_meaningful_contact_resolved, seen_backstory_chapter_ids: snapshot.seen_backstory_chapter_ids, trust: snapshot.stance_profile.trust, warmth: snapshot.stance_profile.warmth, ideological_fit: snapshot.stance_profile.ideological_fit, fear_or_guard: snapshot.stance_profile.fear_or_guard, loyalty: snapshot.stance_profile.loyalty, current_conflict_tag: snapshot.stance_profile.current_conflict_tag, recent_approvals: snapshot.stance_profile.recent_approvals, recent_disapprovals: snapshot.stance_profile.recent_disapprovals, created_at: format_timestamp_micros(snapshot.created_at_micros), updated_at: format_timestamp_micros(snapshot.updated_at_micros), } } pub(crate) fn build_npc_interaction_record( result: DomainNpcInteractionResult, ) -> NpcInteractionRecord { NpcInteractionRecord { npc_state: build_npc_state_record(result.npc_state), interaction_status: format_npc_interaction_status(result.interaction_status).to_string(), action_text: result.action_text, result_text: result.result_text, story_text: result.story_text, battle_mode: result .battle_mode .map(|mode| format_npc_interaction_battle_mode(mode).to_string()), encounter_closed: result.encounter_closed, affinity_changed: result.affinity_changed, previous_affinity: result.previous_affinity, next_affinity: result.next_affinity, } } pub(crate) fn build_npc_battle_interaction_record( result: NpcBattleInteractionSnapshot, ) -> NpcBattleInteractionRecord { NpcBattleInteractionRecord { npc_interaction: build_npc_interaction_record(result.interaction), battle_state: build_battle_state_record(result.battle_state), } } pub(crate) fn format_npc_relation_stance(value: DomainNpcRelationStance) -> &'static str { match value { DomainNpcRelationStance::Hostile => "hostile", DomainNpcRelationStance::Guarded => "guarded", DomainNpcRelationStance::Neutral => "neutral", DomainNpcRelationStance::Cooperative => "cooperative", DomainNpcRelationStance::Bonded => "bonded", } } pub(crate) fn format_npc_interaction_status(value: DomainNpcInteractionStatus) -> &'static str { match value { DomainNpcInteractionStatus::Previewed => "previewed", DomainNpcInteractionStatus::Dialogue => "dialogue", DomainNpcInteractionStatus::Resolved => "resolved", DomainNpcInteractionStatus::Recruited => "recruited", DomainNpcInteractionStatus::BattlePending => "battle_pending", DomainNpcInteractionStatus::Left => "left", } } pub(crate) fn format_npc_interaction_battle_mode( value: DomainNpcInteractionBattleMode, ) -> &'static str { match value { DomainNpcInteractionBattleMode::Fight => "fight", DomainNpcInteractionBattleMode::Spar => "spar", } } pub(crate) fn map_inventory_container_kind( value: InventoryContainerKind, ) -> module_inventory::InventoryContainerKind { match value { InventoryContainerKind::Backpack => module_inventory::InventoryContainerKind::Backpack, InventoryContainerKind::Equipment => module_inventory::InventoryContainerKind::Equipment, } } pub(crate) fn map_inventory_item_rarity( value: InventoryItemRarity, ) -> module_inventory::InventoryItemRarity { match value { InventoryItemRarity::Common => module_inventory::InventoryItemRarity::Common, InventoryItemRarity::Uncommon => module_inventory::InventoryItemRarity::Uncommon, InventoryItemRarity::Rare => module_inventory::InventoryItemRarity::Rare, InventoryItemRarity::Epic => module_inventory::InventoryItemRarity::Epic, InventoryItemRarity::Legendary => module_inventory::InventoryItemRarity::Legendary, } } pub(crate) fn map_inventory_equipment_slot( value: InventoryEquipmentSlot, ) -> module_inventory::InventoryEquipmentSlot { match value { InventoryEquipmentSlot::Weapon => module_inventory::InventoryEquipmentSlot::Weapon, InventoryEquipmentSlot::Armor => module_inventory::InventoryEquipmentSlot::Armor, InventoryEquipmentSlot::Relic => module_inventory::InventoryEquipmentSlot::Relic, } } pub(crate) fn map_inventory_item_source_kind( value: InventoryItemSourceKind, ) -> module_inventory::InventoryItemSourceKind { match value { InventoryItemSourceKind::StoryReward => { module_inventory::InventoryItemSourceKind::StoryReward } InventoryItemSourceKind::QuestReward => { module_inventory::InventoryItemSourceKind::QuestReward } InventoryItemSourceKind::TreasureReward => { module_inventory::InventoryItemSourceKind::TreasureReward } InventoryItemSourceKind::NpcGift => module_inventory::InventoryItemSourceKind::NpcGift, InventoryItemSourceKind::NpcTrade => module_inventory::InventoryItemSourceKind::NpcTrade, InventoryItemSourceKind::CombatDrop => { module_inventory::InventoryItemSourceKind::CombatDrop } InventoryItemSourceKind::ForgeCraft => { module_inventory::InventoryItemSourceKind::ForgeCraft } InventoryItemSourceKind::ForgeReforge => { module_inventory::InventoryItemSourceKind::ForgeReforge } InventoryItemSourceKind::ManualPatch => { module_inventory::InventoryItemSourceKind::ManualPatch } } }