Skip to main content

mingli_contract/
intent.rs

1//! 需求侧:有哪几类问局,各自要什么输入原子、出什么形态。
2//!
3//! 这里**不说谁来答**——那是各叶自己的声明([`crate::CastingEngine::answers`]),
4//! 由编排层在运行时合成。端口层若列出叶名,加一片叶就得回头改端口层,
5//! 而漏改不报错:那片叶只是静默地不入任何路由。
6
7use serde::Serialize;
8
9
10/// 八类问局。
11///
12/// 与 [`crate::QueryKind`] 的关系:`QueryKind` 携带该问局**要哪些输入原子**,`Intent` 只是它的标签,
13/// 用来回答「哪片叶答这一类」。做成枚举而不是字符串,是为了让「一片叶声明它答什么」这件事
14/// 由类型系统盯着——写错一个字符串,那片叶会静默地什么都不答。
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
16#[serde(rename_all = "snake_case")]
17pub enum Intent {
18    /// 命:本命盘(出生切片)。
19    Natal,
20    /// 运:运势 / 流年 / 大运。
21    Fortune,
22    /// 事:占事。
23    Event,
24    /// 择:择吉。
25    Election,
26    /// 合:合盘。
27    Synastry,
28    /// 群/国:国运。
29    Mundane,
30    /// 寻:寻方位。
31    Locative,
32    /// 号:字 / 词(与时刻无关)。
33    Onomancy,
34}
35
36impl Intent {
37    /// 稳定 id(snake_case),与线上字面量一致。
38    #[must_use]
39    pub fn id(self) -> &'static str {
40        match self {
41            Self::Natal => "natal",
42            Self::Fortune => "fortune",
43            Self::Event => "event",
44            Self::Election => "election",
45            Self::Synastry => "synastry",
46            Self::Mundane => "mundane",
47            Self::Locative => "locative",
48            Self::Onomancy => "onomancy",
49        }
50    }
51}
52
53/// 意图的实现状态:Live(已上线)/Pending(结构已声明、算力已在叶里、尚无承接端点)。
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
55pub enum IntentStatus {
56    /// 🟢 已上线:Natal 在所有现有端点工作。
57    Live,
58    /// 🟡 待承接:算力已在叶里,尚无对应端点形态。
59    Pending,
60}
61
62impl IntentStatus {
63    /// 中文标签。
64    #[must_use]
65    pub fn label(self) -> &'static str {
66        match self {
67            Self::Live => "已上线",
68            Self::Pending => "待承接",
69        }
70    }
71}
72
73/// 问局意图规格:每意图所需输入原子 + 默认路由叶 + 输出形态 + 实现状态。
74///
75/// 与 [`crate::DetItem`]/[`crate::SchoolItem`] 同构对偶:profile/schools 声明「怎么算」(供给侧),
76/// intents 声明「被谁调用」(需求侧)。
77#[derive(Debug, Clone, Copy, Serialize)]
78pub struct IntentSpec {
79    /// 这一类问局。
80    pub id: Intent,
81    /// 中文显示名。
82    pub name_zh: &'static str,
83    /// 所需输入原子(instant/geo/sex/seed/text/category/window…),用于 web 表单生成。
84    pub atoms: &'static [&'static str],
85    /// 输出形态(盘/势/断/期/序/配/位)。
86    pub output_shape: &'static str,
87    /// 实现状态。
88    pub status: IntentStatus,
89    /// 一句说明。
90    pub note: &'static str,
91}
92
93/// 构造 [`IntentSpec`] 的简写(crate 私有)。
94const fn i(
95    id: Intent,
96    name_zh: &'static str,
97    atoms: &'static [&'static str],
98    output_shape: &'static str,
99    status: IntentStatus,
100    note: &'static str,
101) -> IntentSpec {
102    IntentSpec { id, name_zh, atoms, output_shape, status, note }
103}
104
105/// 8 类问事意图的清单(声明式,与编排层的 `route` 同构对偶)。
106///
107/// 顺序:Natal / Fortune / Event / Election / Synastry / Mundane / Locative /
108/// Onomancy(D 族字/词,与时刻无关)。
109#[must_use]
110pub fn intents() -> &'static [IntentSpec] {
111    use IntentStatus::Live;
112    const { &[
113        i(
114            Intent::Natal, "命(本命盘)",
115            &["instant", "geo", "sex", "text(name)"],
116            "盘(静态切片,全树并行 fan-out)", Live,
117            "一个时刻的静态切片。全部时刻叶都答这一类,故它也是一片叶不作声明时的缺省",
118        ),
119        i(
120            Intent::Fortune, "运(运势/流年/大运)",
121            &["instant(birth)", "instant(target)", "sex"],
122            "势(时间序列,playhead 切片)", Live,
123            "本命固定、目标时刻在动:同一张底盘上取某一刻的切片,并沿时间轴铺成序列",
124        ),
125        i(
126            Intent::Event, "事(占事)",
127            &["instant(ask)", "seed(draw)", "text(question)"],
128            "断(成败/吉凶/宜忌)", Live,
129            "问事此刻加一次取机;取机的种子入盘,故同一次占问可复现",
130        ),
131        i(
132            Intent::Election, "择(择吉)",
133            &["window(start, end, grain)", "category(婚/葬/动土/行/开业…)"],
134            "期/序(候选日按吉凶排名)", Live,
135            "在一段时窗上逐日取要素并分档。事类宜忌各家出入大,不合成总分;\
136             各家分档的粒度与判据也不同,故这一类目前由单叶作答,合成总排名等于替读者选边",
137        ),
138        i(
139            Intent::Synastry, "合(合盘)",
140            &["instant(a)", "instant(b)", "sex(a,b)"],
141            "配(契合度/互补结构)", Live,
142            "两张本命之间的互供关系,两个方向分别成立,不对称是常态",
143        ),
144        i(
145            Intent::Mundane, "群/国(国运)",
146            &["instant(polity)", "geo"],
147            "势(国运势卜/年度盘)", Live,
148            "以政体奠基时刻为起点的周期结构,沿年份展开;描述的是周期位置,不是对现实的断言",
149        ),
150        i(
151            Intent::Locative, "寻(寻方位)",
152            &["instant(ask)", "seed(draw)", "category(寻人/物/向)"],
153            "位(方位/卦象)", Live,
154            "于问事此刻起课,从盘上抽方位候选(奇门取值符 / 值使 / 三吉门 / 三奇落宫 → 后天八卦方位,六壬取三传或四课上神 → 十二支方位);取用之法各家不同,不合成排名",
155        ),
156        i(
157            Intent::Onomancy, "号(字/词)",
158            &["text(name)", "strokes(姓笔画, 名笔画)"],
159            "号(数字学生命灵数/姓名值/五格)", Live,
160            "唯一不吃时刻的一类:入参是字与笔画,故走 WordEngine 而非 CastingEngine;\
161             数字学同时吃出生日期,因此它两边都在",
162        ),
163    ] }
164}