958 lines
38 KiB
Rust
958 lines
38 KiB
Rust
use super::*;
|
|
|
|
impl From<CustomWorldProfileUpsertRecordInput> 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,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn map_custom_world_profile_list_result(
|
|
result: CustomWorldProfileListResult,
|
|
) -> Result<Vec<CustomWorldLibraryEntryRecord>, 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<CustomWorldLibraryMutationRecord, SpacetimeClientError> {
|
|
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<Vec<CustomWorldGalleryEntryRecord>, 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::<Result<Vec<_>, _>>()?)
|
|
}
|
|
|
|
pub(crate) fn map_custom_world_library_mutation_result(
|
|
result: CustomWorldLibraryMutationResult,
|
|
) -> Result<CustomWorldLibraryMutationRecord, SpacetimeClientError> {
|
|
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<CustomWorldPublishWorldRecord, SpacetimeClientError> {
|
|
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<CustomWorldAgentSessionRecord, SpacetimeClientError> {
|
|
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<CustomWorldAgentOperationRecord, SpacetimeClientError> {
|
|
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<Vec<CustomWorldWorkSummaryRecord>, 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<CustomWorldDraftCardDetailRecord, SpacetimeClientError> {
|
|
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<CustomWorldAgentActionExecuteRecord, SpacetimeClientError> {
|
|
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_custom_world_library_entry_from_profile_snapshot(
|
|
snapshot: CustomWorldProfileSnapshot,
|
|
) -> Result<CustomWorldLibraryEntryRecord, SpacetimeClientError> {
|
|
let profile = serde_json::from_str::<serde_json::Value>(&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<CustomWorldGalleryEntryRecord, SpacetimeClientError> {
|
|
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_gallery_entry_row(
|
|
row: CustomWorldGalleryEntry,
|
|
recent_play_count_7d: u32,
|
|
) -> CustomWorldGalleryEntryRecord {
|
|
CustomWorldGalleryEntryRecord {
|
|
owner_user_id: row.owner_user_id,
|
|
profile_id: row.profile_id,
|
|
public_work_code: row.public_work_code,
|
|
author_public_user_code: row.author_public_user_code,
|
|
visibility: "published".to_string(),
|
|
published_at: Some(format_timestamp_micros(
|
|
row.published_at.to_micros_since_unix_epoch(),
|
|
)),
|
|
updated_at: format_timestamp_micros(row.updated_at.to_micros_since_unix_epoch()),
|
|
author_display_name: row.author_display_name,
|
|
world_name: row.world_name,
|
|
subtitle: row.subtitle,
|
|
summary_text: row.summary_text,
|
|
cover_image_src: row.cover_image_src,
|
|
theme_mode: format_custom_world_theme_mode(map_custom_world_theme_mode_back(
|
|
row.theme_mode,
|
|
))
|
|
.to_string(),
|
|
playable_npc_count: row.playable_npc_count,
|
|
landmark_count: row.landmark_count,
|
|
play_count: row.play_count,
|
|
remix_count: row.remix_count,
|
|
like_count: row.like_count,
|
|
recent_play_count_7d,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn map_custom_world_published_profile_compile_snapshot(
|
|
snapshot: CustomWorldPublishedProfileCompileSnapshot,
|
|
) -> Result<CustomWorldPublishedProfileCompileRecord, SpacetimeClientError> {
|
|
let compiled_profile =
|
|
serde_json::from_str::<serde_json::Value>(&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<CustomWorldWorkSummaryRecord, SpacetimeClientError> {
|
|
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<CustomWorldAgentSessionRecord, SpacetimeClientError> {
|
|
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::<Result<Vec<_>, _>>()?;
|
|
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::<Result<Vec<_>, _>>()?,
|
|
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<CustomWorldDraftCardRecord, SpacetimeClientError> {
|
|
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<CustomWorldDraftCardDetailRecord, SpacetimeClientError> {
|
|
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_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_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_rpg_agent_stage_record(
|
|
value: &str,
|
|
) -> Result<crate::module_bindings::RpgAgentStage, SpacetimeClientError> {
|
|
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<crate::module_bindings::RpgAgentOperationType, SpacetimeClientError> {
|
|
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<crate::module_bindings::RpgAgentOperationStatus, SpacetimeClientError> {
|
|
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()
|
|
}
|
|
|
|
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 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_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_custom_world_checkpoint_record(
|
|
value: serde_json::Value,
|
|
) -> Result<CustomWorldCheckpointRecord, SpacetimeClientError> {
|
|
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_custom_world_publish_gate_record(
|
|
value: &str,
|
|
) -> Result<CustomWorldPublishGateRecord, SpacetimeClientError> {
|
|
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::<Result<Vec<_>, _>>()?;
|
|
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,
|
|
})
|
|
}
|