use std::time::{SystemTime, UNIX_EPOCH}; use axum::{ Json, extract::{Extension, Path, State, rejection::JsonRejection}, http::{HeaderName, StatusCode, header}, response::Response, }; use module_bark_battle::{BARK_BATTLE_RULESET_VERSION_V1, BarkBattleRuleset}; use serde::Deserialize; use serde_json::{Value, json}; use shared_contracts::bark_battle::{ BarkBattleConfigEditorPayload, BarkBattleDerivedMetrics, BarkBattleDifficultyPreset, BarkBattleDraftConfig, BarkBattleDraftCreateRequest, BarkBattleFinishStatus, BarkBattlePublishedConfig, BarkBattleRunFinishRequest, BarkBattleRunFinishResponse, BarkBattleRunStartRequest, BarkBattleRunStartResponse, BarkBattleScoreSummary, BarkBattleServerResult, BarkBattleWorkPublishRequest, }; use shared_kernel::{ build_prefixed_uuid_id, format_rfc3339, format_timestamp_micros, offset_datetime_to_unix_micros, parse_rfc3339, }; use spacetime_client::{ BarkBattleDraftCreateRecordInput, BarkBattleRunFinishRecordInput, BarkBattleRunRecord, BarkBattleRunStartRecordInput, BarkBattleWorkPublishRecordInput, SpacetimeClientError, }; use time::{Duration as TimeDuration, OffsetDateTime}; use crate::{ api_response::json_success_body, auth::AuthenticatedAccessToken, http_error::AppError, request_context::RequestContext, state::AppState, work_play_tracking::{WorkPlayTrackingDraft, record_work_play_start_after_success}, }; const BARK_BATTLE_RUNTIME_PROVIDER: &str = "bark-battle-runtime"; const BARK_BATTLE_DRAFT_ID_PREFIX: &str = "bark-battle-draft-"; const BARK_BATTLE_WORK_ID_PREFIX: &str = "bark-battle-work-"; const BARK_BATTLE_RUN_ID_PREFIX: &str = "bark-battle-run-"; const BARK_BATTLE_RUN_TOKEN_PREFIX: &str = "bark-battle-token-"; const BARK_BATTLE_PLAY_TYPE_ID: &str = "bark-battle"; const BARK_BATTLE_RUN_TTL_SECONDS: i64 = 10 * 60; #[derive(Clone, Debug, Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] struct BarkBattleRunSnapshotRecord { run_id: String, work_id: String, config_version: u64, ruleset_version: String, difficulty_preset: String, #[serde(default)] client_started_at_micros: i64, #[serde(default)] server_started_at_micros: i64, #[serde(default)] server_finished_at_micros: Option, #[serde(default)] metrics_json: String, #[serde(default)] server_result: Option, #[serde(default)] validation_status: String, #[serde(default)] anti_cheat_flags_json: String, #[serde(default)] leaderboard_score: Option, } #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct BarkBattleDraftConfigSnapshotRecord { draft_id: String, #[allow(dead_code)] work_id: String, #[allow(dead_code)] config_version: u64, #[allow(dead_code)] ruleset_version: String, #[serde(default)] config_json: String, updated_at_micros: i64, } #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct BarkBattleRuntimeConfigSnapshotRecord { work_id: String, source_draft_id: Option, config_version: u64, ruleset_version: String, #[serde(default)] config_json: String, published_at_micros: i64, updated_at_micros: i64, } pub async fn create_bark_battle_draft( State(state): State, Extension(request_context): Extension, Extension(authenticated): Extension, payload: Result, JsonRejection>, ) -> Result, Response> { let Json(payload) = bark_battle_json(payload, &request_context)?; let now = current_utc_micros(); let draft = state .spacetime_client() .create_bark_battle_draft(BarkBattleDraftCreateRecordInput { draft_id: build_prefixed_uuid_id(BARK_BATTLE_DRAFT_ID_PREFIX), owner_user_id: authenticated.claims().user_id().to_string(), work_id: build_prefixed_uuid_id(BARK_BATTLE_WORK_ID_PREFIX), title: Some(payload.title), description: payload.description, theme_preset: payload.theme_preset, player_dog_skin_preset: payload.player_dog_skin_preset, opponent_dog_skin_preset: payload.opponent_dog_skin_preset, difficulty_preset: Some( difficulty_to_spacetime_string(&payload.difficulty_preset).to_string(), ), leaderboard_enabled: Some(payload.leaderboard_enabled), editor_state_json: Some("{}".to_string()), created_at_micros: now, }) .await .map_err(|error| { bark_battle_error_response(&request_context, map_bark_battle_client_error(error)) })?; let draft = map_draft_config_record(draft, &request_context)?; Ok(json_success_body(Some(&request_context), draft)) } pub async fn publish_bark_battle_work( State(state): State, Extension(request_context): Extension, Extension(authenticated): Extension, payload: Result, JsonRejection>, ) -> Result, Response> { let Json(payload) = bark_battle_json(payload, &request_context)?; ensure_non_empty(&request_context, &payload.draft_id, "draftId")?; let work_id = payload .work_id .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) .map(ToString::to_string) .unwrap_or_else(|| build_prefixed_uuid_id(BARK_BATTLE_WORK_ID_PREFIX)); let published_snapshot_json = payload .published_snapshot .as_ref() .map(serde_json::to_string) .transpose() .map_err(|error| { bark_battle_error_response( &request_context, AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ "provider": BARK_BATTLE_RUNTIME_PROVIDER, "message": format!("publishedSnapshot JSON 序列化失败: {error}"), })), ) })?; let published = state .spacetime_client() .publish_bark_battle_work(BarkBattleWorkPublishRecordInput { draft_id: payload.draft_id, owner_user_id: authenticated.claims().user_id().to_string(), work_id, published_snapshot_json, published_at_micros: current_utc_micros(), }) .await .map_err(|error| { bark_battle_error_response(&request_context, map_bark_battle_client_error(error)) })?; let published = map_published_config_record(published, &request_context)?; Ok(json_success_body(Some(&request_context), published)) } pub async fn get_bark_battle_runtime_config( State(state): State, Path(work_id): Path, Extension(request_context): Extension, Extension(authenticated): Extension, ) -> Result, Response> { ensure_non_empty(&request_context, &work_id, "workId")?; let config = state .spacetime_client() .get_bark_battle_runtime_config(work_id, Some(authenticated.claims().user_id().to_string())) .await .map_err(|error| { bark_battle_error_response(&request_context, map_bark_battle_client_error(error)) })?; let config = map_runtime_config_record(config, &request_context)?; Ok(json_success_body(Some(&request_context), config)) } pub async fn start_bark_battle_run( State(state): State, Path(work_id): Path, Extension(request_context): Extension, Extension(authenticated): Extension, payload: Result, JsonRejection>, ) -> Result, Response> { let maybe_payload = payload.ok().map(|Json(payload)| payload); let request = maybe_payload.unwrap_or_else(|| BarkBattleRunStartRequest { work_id: work_id.clone(), config_version: None, source_route: None, client_runtime_version: None, }); let work_id = if request.work_id.trim().is_empty() { work_id } else { request.work_id.trim().to_string() }; ensure_non_empty(&request_context, &work_id, "workId")?; let owner_user_id = authenticated.claims().user_id().to_string(); let runtime_config = state .spacetime_client() .get_bark_battle_runtime_config(work_id.clone(), Some(owner_user_id.clone())) .await .map_err(|error| { bark_battle_error_response(&request_context, map_bark_battle_client_error(error)) })?; let runtime_config = map_runtime_config_record(runtime_config, &request_context)?; if !request.work_id.trim().is_empty() && request.work_id.trim() != work_id { return Err(bark_battle_bad_request( &request_context, "workId 与路径参数不一致", )); } if let Some(expected_version) = request.config_version { if expected_version != runtime_config.config_version { return Err(bark_battle_bad_request( &request_context, "configVersion 与已发布配置不一致", )); } } let client_started_at_micros = current_utc_micros(); let run_token = build_prefixed_uuid_id(BARK_BATTLE_RUN_TOKEN_PREFIX); let run = state .spacetime_client() .start_bark_battle_run(BarkBattleRunStartRecordInput { run_id: build_prefixed_uuid_id(BARK_BATTLE_RUN_ID_PREFIX), run_token: run_token.clone(), owner_user_id: owner_user_id.clone(), work_id: work_id.clone(), config_version: u64::from(runtime_config.config_version), ruleset_version: runtime_config.ruleset_version.clone(), difficulty_preset: difficulty_to_spacetime_string(&runtime_config.difficulty_preset) .to_string(), client_started_at_micros, server_started_at_micros: client_started_at_micros, }) .await .map_err(|error| { bark_battle_error_response(&request_context, map_bark_battle_client_error(error)) })?; let run_snapshot = parse_run_record(run, &request_context)?; record_work_play_start_after_success( &state, &request_context, WorkPlayTrackingDraft::new( BARK_BATTLE_PLAY_TYPE_ID, work_id.clone(), &authenticated, "/api/runtime/bark-battle/...", ) .extra(json!({ "runId": run_snapshot.run_id, "workId": work_id, "configVersion": runtime_config.config_version, "rulesetVersion": runtime_config.ruleset_version, "difficultyPreset": runtime_config.difficulty_preset, "sourceRoute": request.source_route, "clientRuntimeVersion": request.client_runtime_version, })), ) .await; let server_started_at = format_timestamp_micros(run_snapshot.server_started_at_micros); let expires_at = format_timestamp_micros( run_snapshot .server_started_at_micros .saturating_add(BARK_BATTLE_RUN_TTL_SECONDS * 1_000_000), ); Ok(json_success_body( Some(&request_context), BarkBattleRunStartResponse { run_id: run_snapshot.run_id, run_token, work_id: run_snapshot.work_id, config_version: runtime_config.config_version, ruleset_version: runtime_config.ruleset_version.clone(), difficulty_preset: runtime_config.difficulty_preset.clone(), runtime_config, server_started_at, expires_at, }, )) } pub async fn get_bark_battle_run( State(state): State, Path(run_id): Path, Extension(request_context): Extension, Extension(authenticated): Extension, ) -> Result, Response> { ensure_non_empty(&request_context, &run_id, "runId")?; let run = state .spacetime_client() .get_bark_battle_run(run_id, authenticated.claims().user_id().to_string()) .await .map_err(|error| { bark_battle_error_response(&request_context, map_bark_battle_client_error(error)) })?; let run = parse_run_record(run, &request_context)?; Ok(json_success_body(Some(&request_context), run)) } pub async fn finish_bark_battle_run( State(state): State, Path(run_id): Path, Extension(request_context): Extension, Extension(authenticated): Extension, payload: Result, JsonRejection>, ) -> Result, Response> { let Json(payload) = bark_battle_json(payload, &request_context)?; ensure_non_empty(&request_context, &run_id, "runId")?; ensure_non_empty(&request_context, &payload.work_id, "workId")?; ensure_non_empty(&request_context, &payload.run_token, "runToken")?; if payload.run_id != run_id { return Err(bark_battle_bad_request( &request_context, "runId 与路径参数不一致", )); } if payload.ruleset_version != BARK_BATTLE_RULESET_VERSION_V1 { return Err(bark_battle_bad_request( &request_context, "rulesetVersion 不支持", )); } let client_finished_at_micros = parse_client_time_to_micros(&payload.client_finished_at) .map_err(|message| bark_battle_bad_request(&request_context, &message))?; let derived = &payload.derived_metrics; let opponent_final_energy = derive_server_opponent_final_energy(derived); let metrics_json = serde_json::to_string(&json!({ "clientStartedAt": payload.client_started_at, "clientFinishedAt": payload.client_finished_at, "durationMs": payload.duration_ms, "derivedMetrics": payload.derived_metrics, "clientResult": payload.client_result, "sampleDigest": payload.sample_digest, "clientRuntimeVersion": payload.client_runtime_version, })) .unwrap_or_else(|_| "{}".to_string()); let derived_metrics_json = serde_json::to_string(derived).unwrap_or_else(|_| "{}".to_string()); let run = state .spacetime_client() .finish_bark_battle_run(BarkBattleRunFinishRecordInput { run_id, run_token: payload.run_token, owner_user_id: authenticated.claims().user_id().to_string(), work_id: payload.work_id.clone(), config_version: u64::from(payload.config_version), ruleset_version: payload.ruleset_version.clone(), difficulty_preset: difficulty_to_spacetime_string(&payload.difficulty_preset) .to_string(), client_finished_at_micros, server_finished_at_micros: current_utc_micros(), duration_ms: payload.duration_ms, trigger_count: u64::from(derived.trigger_count), max_volume_millis: unit_to_millis(derived.max_volume), average_volume_millis: unit_to_millis(derived.average_volume), final_energy_millis: energy_to_millis(derived.final_energy), opponent_final_energy_millis: energy_to_millis(opponent_final_energy), max_combo: derived.combo_max, metrics_json, derived_metrics_json, }) .await .map_err(|error| { bark_battle_error_response(&request_context, map_bark_battle_client_error(error)) })?; let run = parse_run_record(run, &request_context)?; Ok(json_success_body( Some(&request_context), map_finish_response(run, &payload.derived_metrics), )) } fn map_finish_response( run: BarkBattleRunSnapshotRecord, fallback_metrics: &BarkBattleDerivedMetrics, ) -> BarkBattleRunFinishResponse { let score_summary = parse_score_summary(&run.metrics_json).unwrap_or_else(|| BarkBattleScoreSummary { duration_ms: 0, trigger_count: fallback_metrics.trigger_count, max_volume: fallback_metrics.max_volume, average_volume: fallback_metrics.average_volume, final_energy: fallback_metrics.final_energy, combo_max: fallback_metrics.combo_max, }); BarkBattleRunFinishResponse { status: parse_finish_status(&run.validation_status), run_id: run.run_id, work_id: run.work_id, config_version: run.config_version.min(u64::from(u32::MAX)) as u32, ruleset_version: run.ruleset_version, difficulty_preset: parse_difficulty_lossy(&run.difficulty_preset), server_result: parse_server_result_lossy(run.server_result.as_deref()), score_summary, leaderboard_score: run.leaderboard_score, anti_cheat_flags: parse_string_vec(&run.anti_cheat_flags_json), updated_at: format_timestamp_micros( run.server_finished_at_micros .unwrap_or(run.server_started_at_micros), ), } } fn parse_run_record( value: BarkBattleRunRecord, request_context: &RequestContext, ) -> Result { serde_json::from_value(value).map_err(|error| { bark_battle_error_response( request_context, AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ "provider": BARK_BATTLE_RUNTIME_PROVIDER, "message": format!("Bark Battle run JSON 解析失败: {error}"), })), ) }) } fn parse_draft_snapshot_record( value: Value, request_context: &RequestContext, ) -> Result { serde_json::from_value(value) .map_err(|error| bark_battle_snapshot_parse_error(request_context, "draft config", error)) } fn parse_runtime_snapshot_record( value: Value, request_context: &RequestContext, ) -> Result { serde_json::from_value(value) .map_err(|error| bark_battle_snapshot_parse_error(request_context, "runtime config", error)) } fn map_draft_config_record( value: Value, request_context: &RequestContext, ) -> Result { let snapshot = parse_draft_snapshot_record(value, request_context)?; let editor_config = parse_editor_config_record(&snapshot.config_json, request_context)?; Ok(BarkBattleDraftConfig { draft_id: snapshot.draft_id, title: editor_config.title, description: editor_config.description, theme_preset: editor_config.theme_preset, player_dog_skin_preset: editor_config.player_dog_skin_preset, opponent_dog_skin_preset: editor_config.opponent_dog_skin_preset, difficulty_preset: editor_config.difficulty_preset, leaderboard_enabled: editor_config.leaderboard_enabled, updated_at: format_timestamp_micros(snapshot.updated_at_micros), }) } fn map_runtime_config_record( value: Value, request_context: &RequestContext, ) -> Result { let snapshot = parse_runtime_snapshot_record(value, request_context)?; let editor_config = parse_editor_config_record(&snapshot.config_json, request_context)?; let ruleset = BarkBattleRuleset::v1(); Ok(shared_contracts::bark_battle::BarkBattleRuntimeConfig { work_id: snapshot.work_id, config_version: snapshot.config_version.min(u64::from(u32::MAX)) as u32, ruleset_version: snapshot.ruleset_version, play_type_id: BARK_BATTLE_PLAY_TYPE_ID.to_string(), duration_ms: ruleset.standard_duration_ms, energy_min: 0.0, energy_max: 100.0, draw_threshold: ruleset.draw_threshold_energy as f32, min_bark_gap_ms: ruleset.min_bark_gap_ms, difficulty_preset: editor_config.difficulty_preset, theme_preset: editor_config.theme_preset, player_dog_skin_preset: editor_config.player_dog_skin_preset, opponent_dog_skin_preset: editor_config.opponent_dog_skin_preset, leaderboard_enabled: editor_config.leaderboard_enabled, updated_at: format_timestamp_micros(snapshot.updated_at_micros), }) } fn map_published_config_record( value: Value, request_context: &RequestContext, ) -> Result { let snapshot = parse_runtime_snapshot_record(value, request_context)?; let editor_config = parse_editor_config_record(&snapshot.config_json, request_context)?; Ok(BarkBattlePublishedConfig { work_id: snapshot.work_id, draft_id: snapshot.source_draft_id, config_version: snapshot.config_version.min(u64::from(u32::MAX)) as u32, ruleset_version: snapshot.ruleset_version, play_type_id: BARK_BATTLE_PLAY_TYPE_ID.to_string(), title: editor_config.title, description: editor_config.description, theme_preset: editor_config.theme_preset, player_dog_skin_preset: editor_config.player_dog_skin_preset, opponent_dog_skin_preset: editor_config.opponent_dog_skin_preset, difficulty_preset: editor_config.difficulty_preset, leaderboard_enabled: editor_config.leaderboard_enabled, updated_at: format_timestamp_micros(snapshot.updated_at_micros), published_at: format_timestamp_micros(snapshot.published_at_micros), }) } fn parse_editor_config_record( config_json: &str, request_context: &RequestContext, ) -> Result { serde_json::from_str(config_json).map_err(|error| { bark_battle_error_response( request_context, AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ "provider": BARK_BATTLE_RUNTIME_PROVIDER, "message": format!("Bark Battle configJson 解析失败: {error}"), })), ) }) } fn bark_battle_snapshot_parse_error( request_context: &RequestContext, label: &str, error: serde_json::Error, ) -> Response { bark_battle_error_response( request_context, AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ "provider": BARK_BATTLE_RUNTIME_PROVIDER, "message": format!("Bark Battle {label} JSON 解析失败: {error}"), })), ) } fn bark_battle_json( payload: Result, JsonRejection>, request_context: &RequestContext, ) -> Result, Response> { payload.map_err(|error| { bark_battle_error_response( request_context, AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ "provider": BARK_BATTLE_RUNTIME_PROVIDER, "message": error.body_text(), })), ) }) } fn ensure_non_empty( request_context: &RequestContext, value: &str, field_name: &str, ) -> Result<(), Response> { if value.trim().is_empty() { return Err(bark_battle_bad_request( request_context, &format!("{field_name} is required"), )); } Ok(()) } fn bark_battle_bad_request(request_context: &RequestContext, message: &str) -> Response { bark_battle_error_response( request_context, AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ "provider": BARK_BATTLE_RUNTIME_PROVIDER, "message": message, })), ) } fn map_bark_battle_client_error(error: SpacetimeClientError) -> AppError { let status = match &error { SpacetimeClientError::Runtime(_) => StatusCode::BAD_REQUEST, SpacetimeClientError::Procedure(message) if message.contains("不存在") || message.contains("not found") || message.contains("does not exist") => { StatusCode::NOT_FOUND } SpacetimeClientError::Procedure(message) if message.contains("不能为空") || message.contains("不匹配") || message.contains("不支持") || message.contains("已结束") || message.contains("已存在") => { StatusCode::BAD_REQUEST } _ => StatusCode::BAD_GATEWAY, }; AppError::from_status(status).with_details(json!({ "provider": "spacetimedb", "message": error.to_string(), })) } fn bark_battle_error_response(request_context: &RequestContext, error: AppError) -> Response { let mut response = error.into_response_with_context(Some(request_context)); response.headers_mut().insert( HeaderName::from_static("x-genarrative-provider"), header::HeaderValue::from_static(BARK_BATTLE_RUNTIME_PROVIDER), ); response } fn parse_client_time_to_micros(value: &str) -> Result { let trimmed = value.trim(); if trimmed.is_empty() { return Err("client timestamp is required".to_string()); } if let Ok(micros) = trimmed.parse::() { return Ok(micros); } parse_rfc3339(trimmed).map(offset_datetime_to_unix_micros) } fn current_utc_micros() -> i64 { let duration = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default(); (duration.as_secs() as i64) * 1_000_000 + i64::from(duration.subsec_micros()) } fn unit_to_millis(value: f32) -> u32 { (value.clamp(0.0, 1.0) * 1_000.0).round() as u32 } fn energy_to_millis(value: f32) -> u32 { (value.clamp(0.0, 100.0) * 1_000.0).round() as u32 } fn derive_server_opponent_final_energy(metrics: &BarkBattleDerivedMetrics) -> f32 { let ruleset = BarkBattleRuleset::v1(); let pressure = (metrics.average_volume * 24.0) + (metrics.max_volume * 16.0) + (metrics.trigger_count as f32 * 0.35) + (metrics.combo_max as f32 * 0.2); (ruleset.max_final_energy - pressure).clamp(ruleset.min_final_energy, ruleset.max_final_energy) } fn difficulty_to_spacetime_string(value: &BarkBattleDifficultyPreset) -> &'static str { match value { BarkBattleDifficultyPreset::Easy => "easy", BarkBattleDifficultyPreset::Normal => "normal", BarkBattleDifficultyPreset::Hard => "hard", } } fn parse_difficulty(value: &str) -> Result { match value { "easy" => Ok(BarkBattleDifficultyPreset::Easy), "normal" => Ok(BarkBattleDifficultyPreset::Normal), "hard" => Ok(BarkBattleDifficultyPreset::Hard), _ => Err( AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ "provider": BARK_BATTLE_RUNTIME_PROVIDER, "message": format!("Bark Battle difficultyPreset 不支持: {value}"), })), ), } } fn parse_difficulty_lossy(value: &str) -> BarkBattleDifficultyPreset { parse_difficulty(value).unwrap_or(BarkBattleDifficultyPreset::Normal) } fn parse_finish_status(value: &str) -> BarkBattleFinishStatus { match value { "accepted" => BarkBattleFinishStatus::Accepted, "accepted_with_flags" => BarkBattleFinishStatus::AcceptedWithFlags, "rejected" => BarkBattleFinishStatus::Rejected, _ => BarkBattleFinishStatus::Rejected, } } fn parse_server_result_lossy(value: Option<&str>) -> BarkBattleServerResult { match value { Some("player_win") => BarkBattleServerResult::PlayerWin, Some("opponent_win") => BarkBattleServerResult::OpponentWin, Some("draw") => BarkBattleServerResult::Draw, _ => BarkBattleServerResult::Draw, } } fn parse_score_summary(metrics_json: &str) -> Option { let value: Value = serde_json::from_str(metrics_json).ok()?; let derived = value.get("derivedMetrics")?; Some(BarkBattleScoreSummary { duration_ms: value.get("durationMs")?.as_u64()?, trigger_count: derived .get("triggerCount")? .as_u64()? .min(u64::from(u32::MAX)) as u32, max_volume: derived.get("maxVolume")?.as_f64()? as f32, average_volume: derived.get("averageVolume")?.as_f64()? as f32, final_energy: derived.get("finalEnergy")?.as_f64()? as f32, combo_max: derived.get("comboMax")?.as_u64()?.min(u64::from(u32::MAX)) as u32, }) } fn parse_string_vec(value: &str) -> Vec { serde_json::from_str(value).unwrap_or_default() } #[allow(dead_code)] fn format_rfc3339_or_timestamp_micros(micros: i64) -> String { let seconds = micros.div_euclid(1_000_000); let subsec_micros = micros.rem_euclid(1_000_000); let Ok(value) = OffsetDateTime::from_unix_timestamp(seconds) .map(|value| value + TimeDuration::microseconds(subsec_micros)) else { return format_timestamp_micros(micros); }; format_rfc3339(value).unwrap_or_else(|_| format_timestamp_micros(micros)) } #[cfg(test)] mod tests { use super::*; #[test] fn unit_and_energy_are_clamped_to_spacetime_millis() { assert_eq!(unit_to_millis(0.625), 625); assert_eq!(unit_to_millis(3.0), 1000); assert_eq!(energy_to_millis(88.456), 88_456); assert_eq!(energy_to_millis(120.0), 100_000); } #[test] fn parses_rfc3339_and_numeric_client_timestamps() { assert_eq!( parse_client_time_to_micros("1713686401234567").unwrap(), 1_713_686_401_234_567 ); assert_eq!( parse_client_time_to_micros("2024-04-21T04:00:01.234567Z").unwrap(), 1_713_672_001_234_567 ); } }