feat: 零点原核频道v1+登录双门地基+五湖开场——zero_point裁决链/灯塔查号/code_repo_login/通知卡重设计/浅色主题联动(冰朔20260815夜谕)
This commit is contained in:
parent
ffed065841
commit
07fd4c85ef
16 changed files with 3082 additions and 88 deletions
|
|
@ -0,0 +1,266 @@
|
|||
//! GLP 标准信封 · glp_envelope
|
||||
//!
|
||||
//! 协议转工程第一件(HoloLake第二阶段总体规划-20260815 · 施工总纲):
|
||||
//! GLS-0300《GLP 通信核心协议》第3节"标准消息结构"的逐字段工程映射。
|
||||
//! 字段一个不造、一个不丢——老家谱 YAML 原文即本文件的形状。
|
||||
//!
|
||||
//! 指挥链落点(铁律四):人格体→宿主的一切指令都必须是这个信封;
|
||||
//! 宿主只认信封不认散话。
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
/// GLS-0300 · receiver.routing_mode
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RoutingMode {
|
||||
Direct,
|
||||
Channel,
|
||||
Broadcast,
|
||||
}
|
||||
|
||||
/// GLS-0300 · payload.content_type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ContentType {
|
||||
Text,
|
||||
Command,
|
||||
Event,
|
||||
State,
|
||||
Reference,
|
||||
}
|
||||
|
||||
/// GLS-0300 · control.priority
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Priority {
|
||||
Low,
|
||||
Normal,
|
||||
High,
|
||||
Critical,
|
||||
}
|
||||
|
||||
/// GLS-0300 · control.retry_policy
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RetryPolicy {
|
||||
None,
|
||||
Safe,
|
||||
Guaranteed,
|
||||
}
|
||||
|
||||
/// GLS-0300 · sender
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GlpSender {
|
||||
pub object_id: String,
|
||||
pub object_type: String,
|
||||
#[serde(default)]
|
||||
pub world_path: String,
|
||||
}
|
||||
|
||||
/// GLS-0300 · receiver
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GlpReceiver {
|
||||
pub object_id: String,
|
||||
pub object_type: String,
|
||||
pub routing_mode: RoutingMode,
|
||||
}
|
||||
|
||||
/// GLS-0300 · context(hldp_anchor 即铁律二的记忆锚点)
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GlpContext {
|
||||
#[serde(default)]
|
||||
pub conversation_id: String,
|
||||
#[serde(default)]
|
||||
pub parent_message_id: String,
|
||||
#[serde(default)]
|
||||
pub relation_id: String,
|
||||
#[serde(default)]
|
||||
pub task_id: String,
|
||||
#[serde(default)]
|
||||
pub hldp_anchor: String,
|
||||
}
|
||||
|
||||
/// GLS-0300 · payload
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GlpPayload {
|
||||
pub language: String,
|
||||
pub content_type: ContentType,
|
||||
pub content: String,
|
||||
#[serde(default)]
|
||||
pub attachments: Vec<String>,
|
||||
}
|
||||
|
||||
/// GLS-0300 · control
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GlpControl {
|
||||
pub priority: Priority,
|
||||
pub ack_required: bool,
|
||||
pub receipt_required: bool,
|
||||
#[serde(default)]
|
||||
pub expires_at: String,
|
||||
pub retry_policy: RetryPolicy,
|
||||
}
|
||||
|
||||
/// GLS-0300 · integrity(高风险消息须带签名与回执链——通信安全节)
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GlpIntegrity {
|
||||
#[serde(default)]
|
||||
pub checksum: String,
|
||||
#[serde(default)]
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
/// GLS-0300 · glp_message 全信封
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GlpMessage {
|
||||
pub protocol: String,
|
||||
pub message_id: String,
|
||||
pub message_type: String,
|
||||
pub created_at: String,
|
||||
pub sender: GlpSender,
|
||||
pub receiver: GlpReceiver,
|
||||
pub context: GlpContext,
|
||||
pub payload: GlpPayload,
|
||||
pub control: GlpControl,
|
||||
pub integrity: GlpIntegrity,
|
||||
}
|
||||
|
||||
/// 信封进门第一道验:协议号、编号格式、收发主体必须在场。
|
||||
/// 不合格的信封宿主不收——指挥链只认标准件。
|
||||
pub fn validate_envelope(message: &GlpMessage) -> Result<(), String> {
|
||||
if message.protocol != "GLP/1.0" {
|
||||
return Err("HOLOLAKE_GLP_PROTOCOL_UNKNOWN".into());
|
||||
}
|
||||
if message.message_id.trim().is_empty() || message.created_at.trim().is_empty() {
|
||||
return Err("HOLOLAKE_GLP_ENVELOPE_INCOMPLETE".into());
|
||||
}
|
||||
if message.sender.object_id.trim().is_empty() || message.sender.object_type.trim().is_empty() {
|
||||
return Err("HOLOLAKE_GLP_SENDER_INCOMPLETE".into());
|
||||
}
|
||||
if message.receiver.object_id.trim().is_empty()
|
||||
|| message.receiver.object_type.trim().is_empty()
|
||||
{
|
||||
return Err("HOLOLAKE_GLP_RECEIVER_INCOMPLETE".into());
|
||||
}
|
||||
if message.payload.content.is_empty() {
|
||||
return Err("HOLOLAKE_GLP_PAYLOAD_EMPTY".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 消息编号按老家谱格式生成:GLP-MSG-YYYYMMDD-000001。
|
||||
/// 序号由账本(管家层)当日累计给出,这里只拼形状。
|
||||
pub fn build_message_id(date_compact: &str, daily_sequence: u64) -> String {
|
||||
format!("GLP-MSG-{date_compact}-{daily_sequence:06}")
|
||||
}
|
||||
|
||||
/// ISO-8601 近似时刻戳(秒级,UTC)——老家谱要 ISO-8601,工程给秒级事实。
|
||||
pub fn now_iso8601() -> String {
|
||||
let secs = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|duration| duration.as_secs())
|
||||
.unwrap_or(0);
|
||||
let days = secs / 86_400;
|
||||
let rem = secs % 86_400;
|
||||
let (hours, minutes, seconds) = (rem / 3_600, (rem % 3_600) / 60, rem % 60);
|
||||
let (year, month, day) = civil_from_days(days as i64);
|
||||
format!("{year:04}-{month:02}-{day:02}T{hours:02}:{minutes:02}:{seconds:02}Z")
|
||||
}
|
||||
|
||||
/// 1970-01-01 起的天数转公历年月日(Howard Hinnant 算法,纯本地实现不引新依赖)。
|
||||
fn civil_from_days(z: i64) -> (i64, u32, u32) {
|
||||
let z = z + 719_468;
|
||||
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
|
||||
let doe = (z - era * 146_097) as u64;
|
||||
let yoe = (doe - doe / 1_460 + doe / 36_524 - doe / 146_096) / 365;
|
||||
let y = yoe as i64 + era * 400;
|
||||
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||
let mp = (5 * doy + 2) / 153;
|
||||
let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
|
||||
let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32;
|
||||
(if m <= 2 { y + 1 } else { y }, m, d)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn sample_envelope() -> GlpMessage {
|
||||
GlpMessage {
|
||||
protocol: "GLP/1.0".into(),
|
||||
message_id: build_message_id("20260815", 1),
|
||||
message_type: "DIRECT".into(),
|
||||
created_at: now_iso8601(),
|
||||
sender: GlpSender {
|
||||
object_id: "AGE-0001".into(),
|
||||
object_type: "age".into(),
|
||||
world_path: "glw://tolaria".into(),
|
||||
},
|
||||
receiver: GlpReceiver {
|
||||
object_id: "HOLOLAKE-HOST".into(),
|
||||
object_type: "host".into(),
|
||||
routing_mode: RoutingMode::Direct,
|
||||
},
|
||||
context: GlpContext {
|
||||
task_id: "ZY-TEST".into(),
|
||||
hldp_anchor: "ZY-CHECKPOINT-20260815-014".into(),
|
||||
..Default::default()
|
||||
},
|
||||
payload: GlpPayload {
|
||||
language: "zh-CN".into(),
|
||||
content_type: ContentType::Command,
|
||||
content: "开工".into(),
|
||||
attachments: vec![],
|
||||
},
|
||||
control: GlpControl {
|
||||
priority: Priority::Normal,
|
||||
ack_required: true,
|
||||
receipt_required: true,
|
||||
expires_at: String::new(),
|
||||
retry_policy: RetryPolicy::None,
|
||||
},
|
||||
integrity: GlpIntegrity::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn envelope_round_trip_keeps_every_field() {
|
||||
let message = sample_envelope();
|
||||
let json = serde_json::to_string(&message).unwrap();
|
||||
let back: GlpMessage = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(back.protocol, "GLP/1.0");
|
||||
assert_eq!(back.sender.object_id, "AGE-0001");
|
||||
assert_eq!(back.receiver.routing_mode, RoutingMode::Direct);
|
||||
assert_eq!(back.payload.content_type, ContentType::Command);
|
||||
assert_eq!(back.context.hldp_anchor, "ZY-CHECKPOINT-20260815-014");
|
||||
assert!(back.control.receipt_required);
|
||||
validate_envelope(&back).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_id_follows_family_format() {
|
||||
assert_eq!(build_message_id("20260815", 7), "GLP-MSG-20260815-000007");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_protocol_is_rejected() {
|
||||
let mut message = sample_envelope();
|
||||
message.protocol = "GLP/0.9".into();
|
||||
assert!(validate_envelope(&message).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_payload_is_rejected() {
|
||||
let mut message = sample_envelope();
|
||||
message.payload.content = String::new();
|
||||
assert!(validate_envelope(&message).is_err());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue