Files
Genarrative/server-rs/crates/module-story/src/commands.rs

149 lines
5.0 KiB
Rust

//! 剧情写入命令与输入归一化。
//!
//! 命令层只处理 story session scoped 的输入结构、字段裁剪和基础校验,不读取数据库,
//! 也不兼容旧 `/api/runtime/story/*` 总入口。
use crate::errors::StorySessionFieldError;
use serde::{Deserialize, Serialize};
use shared_kernel::{
normalize_optional_string as normalize_shared_optional_string, normalize_required_string,
};
#[cfg(feature = "spacetime-types")]
use spacetimedb::SpacetimeType;
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StorySessionInput {
pub story_session_id: String,
pub runtime_session_id: String,
pub actor_user_id: String,
pub world_profile_id: String,
pub initial_prompt: String,
pub opening_summary: Option<String>,
pub created_at_micros: i64,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StoryContinueInput {
pub story_session_id: String,
pub event_id: String,
pub narrative_text: String,
pub choice_function_id: Option<String>,
pub updated_at_micros: i64,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StorySessionStateInput {
pub story_session_id: String,
}
pub fn build_story_session_input(
story_session_id: String,
runtime_session_id: String,
actor_user_id: String,
world_profile_id: String,
initial_prompt: String,
opening_summary: Option<String>,
created_at_micros: i64,
) -> Result<StorySessionInput, StorySessionFieldError> {
let input = StorySessionInput {
story_session_id: normalize_required_string(story_session_id).unwrap_or_default(),
runtime_session_id: normalize_required_string(runtime_session_id).unwrap_or_default(),
actor_user_id: normalize_required_string(actor_user_id).unwrap_or_default(),
world_profile_id: normalize_required_string(world_profile_id).unwrap_or_default(),
initial_prompt: normalize_required_string(initial_prompt).unwrap_or_default(),
opening_summary: normalize_optional_value(opening_summary),
created_at_micros,
};
validate_story_session_input(&input)?;
Ok(input)
}
pub fn build_story_session_state_input(
story_session_id: String,
) -> Result<StorySessionStateInput, StorySessionFieldError> {
let input = StorySessionStateInput {
story_session_id: normalize_required_string(story_session_id).unwrap_or_default(),
};
validate_story_session_state_input(&input)?;
Ok(input)
}
pub fn build_story_continue_input(
story_session_id: String,
event_id: String,
narrative_text: String,
choice_function_id: Option<String>,
updated_at_micros: i64,
) -> Result<StoryContinueInput, StorySessionFieldError> {
let input = StoryContinueInput {
story_session_id: normalize_required_string(story_session_id).unwrap_or_default(),
event_id: normalize_required_string(event_id).unwrap_or_default(),
narrative_text: normalize_required_string(narrative_text).unwrap_or_default(),
choice_function_id: normalize_optional_value(choice_function_id),
updated_at_micros,
};
validate_story_continue_input(&input)?;
Ok(input)
}
pub fn validate_story_session_input(
input: &StorySessionInput,
) -> Result<(), StorySessionFieldError> {
if normalize_required_string(&input.story_session_id).is_none() {
return Err(StorySessionFieldError::MissingSessionId);
}
if normalize_required_string(&input.runtime_session_id).is_none() {
return Err(StorySessionFieldError::MissingRuntimeSessionId);
}
if normalize_required_string(&input.actor_user_id).is_none() {
return Err(StorySessionFieldError::MissingActorUserId);
}
if normalize_required_string(&input.world_profile_id).is_none() {
return Err(StorySessionFieldError::MissingWorldProfileId);
}
if normalize_required_string(&input.initial_prompt).is_none() {
return Err(StorySessionFieldError::MissingInitialPrompt);
}
Ok(())
}
pub fn validate_story_session_state_input(
input: &StorySessionStateInput,
) -> Result<(), StorySessionFieldError> {
if normalize_required_string(&input.story_session_id).is_none() {
return Err(StorySessionFieldError::MissingSessionId);
}
Ok(())
}
pub fn validate_story_continue_input(
input: &StoryContinueInput,
) -> Result<(), StorySessionFieldError> {
if normalize_required_string(&input.story_session_id).is_none() {
return Err(StorySessionFieldError::MissingSessionId);
}
if normalize_required_string(&input.event_id).is_none() {
return Err(StorySessionFieldError::MissingEventId);
}
if normalize_required_string(&input.narrative_text).is_none() {
return Err(StorySessionFieldError::MissingNarrativeText);
}
Ok(())
}
pub fn normalize_optional_value(value: Option<String>) -> Option<String> {
normalize_shared_optional_string(value)
}