Skip to main content

nichlink/registry_core/requirements/
requirements.rs

1//! Capability requirements analysis over a registration declaration set.
2//! Pure graph logic: the caller collects declarations and requirements from
3//! wherever they live (build scans files, tools read snapshots) and the
4//! kernel decides which requirements miss an ancestor provider.
5//! 对注册声明集合做能力需求分析。纯图逻辑:由调用方从任意来源收集
6//! 声明与需求(build 扫文件、工具读快照),kernel 判定哪些需求
7//! 在祖先链上找不到提供者。
8
9use std::collections::BTreeSet;
10
11use crate::registry_core::diagnostic::BuildDiagnostic;
12use crate::registry_core::identity::NodeId;
13
14/// One registration declaration that may provide capabilities to its subtree.
15/// 一条可能向子树提供能力的注册声明。
16#[derive(Clone, Debug)]
17pub struct CapabilityDeclaration {
18    /// Identity of the declaring face.
19    /// 作出声明的注册面身份。
20    pub id: NodeId,
21    /// Face this declaration registers under; `None` for a root declaration.
22    /// 该声明所挂载的父面;根声明为 `None`。
23    pub parent: Option<NodeId>,
24    /// Registration kind a requirement's expected provider must match.
25    /// 需求所期望的提供者必须匹配的注册类型。
26    pub kind: String,
27    /// Capability names this declaration offers to its subtree.
28    /// 该声明向其子树提供的能力名称。
29    pub provides: Vec<String>,
30}
31
32/// One `requires = ...` entry collected from a registration face.
33/// 从注册面收集到的一条 `requires = ...` 需求。
34#[derive(Clone, Debug)]
35pub struct CapabilityRequirement {
36    /// Identity of the face that declares the requirement.
37    /// 声明该需求的注册面身份。
38    pub node: NodeId,
39    /// Registration kind of the requiring face.
40    /// 需求方注册面的注册类型。
41    pub kind: String,
42    /// Logical function or handle the requirement was written in.
43    /// 书写该需求所在的逻辑函数或 handle。
44    pub function: String,
45    /// Graft branch the requirement was collected under.
46    /// 收集该需求时所在的 graft 分支。
47    pub branch: String,
48    /// Capability name the face needs from an ancestor.
49    /// 该注册面需要祖先提供的能力名称。
50    pub capability: String,
51    /// Expected registration kind of the ancestor that must provide it.
52    /// 必须提供该能力的祖先的期望注册类型。
53    pub provider: String,
54    /// Where the ancestor search starts; `None` means no ancestor can satisfy
55    /// it, so the requirement is always reported.
56    /// 祖先搜索的起点;`None` 表示没有祖先能满足它,因此该需求总会被报告。
57    pub parent: Option<NodeId>,
58    /// Source file the requirement was written in.
59    /// 书写该需求的源文件。
60    pub source: String,
61    /// 1-based line inside `source`.
62    /// `source` 内以 1 起始的行号。
63    pub line: usize,
64}
65
66/// Every requirement whose ancestor chain offers no matching provider.
67/// 祖先链上找不到匹配提供者的全部需求,逐条生成诊断。
68pub fn missing_capabilities(
69    requirements: Vec<CapabilityRequirement>,
70    declarations: &[CapabilityDeclaration],
71) -> Vec<BuildDiagnostic> {
72    requirements
73        .into_iter()
74        .filter_map(|requirement| missing(requirement, declarations))
75        .collect()
76}
77
78fn missing(
79    requirement: CapabilityRequirement,
80    declarations: &[CapabilityDeclaration],
81) -> Option<BuildDiagnostic> {
82    let provided = requirement.parent.is_some_and(|parent| {
83        has_ancestor_provider(
84            parent,
85            &requirement.capability,
86            &requirement.provider,
87            declarations,
88        )
89    });
90    if provided {
91        return None;
92    }
93    let detail = requirement
94        .parent
95        .and_then(|parent| find_ancestor_capability(parent, &requirement.capability, declarations))
96        .map(|declaration| declaration.kind.clone());
97    let mut diagnostic = BuildDiagnostic::new(
98        "requirements",
99        format!("missing capability `{}`", requirement.capability),
100    )
101    .branch(requirement.branch)
102    .node(requirement.node.to_string(), requirement.kind)
103    .at(requirement.source, requirement.line)
104    .function(requirement.function)
105    .field(requirement.capability)
106    .expected(requirement.provider.clone());
107    if let Some(provider) = detail {
108        diagnostic = diagnostic.provider(provider);
109    }
110    Some(diagnostic)
111}
112
113fn has_ancestor_provider(
114    mut target: NodeId,
115    capability: &str,
116    expected_kind: &str,
117    declarations: &[CapabilityDeclaration],
118) -> bool {
119    let mut visited = BTreeSet::new();
120    loop {
121        if !visited.insert(target) {
122            return false;
123        }
124        if declarations.iter().any(|declaration| {
125            declaration.id == target
126                && declaration.kind == expected_kind
127                && declaration.provides.iter().any(|item| item == capability)
128        }) {
129            return true;
130        }
131        let Some(parent) = declarations
132            .iter()
133            .find(|declaration| declaration.id == target)
134            .and_then(|declaration| declaration.parent)
135        else {
136            return false;
137        };
138        if parent == target {
139            return false;
140        }
141        target = parent;
142    }
143}
144
145fn find_ancestor_capability<'a>(
146    mut target: NodeId,
147    capability: &str,
148    declarations: &'a [CapabilityDeclaration],
149) -> Option<&'a CapabilityDeclaration> {
150    let mut visited = BTreeSet::new();
151    loop {
152        if !visited.insert(target) {
153            return None;
154        }
155        if let Some(declaration) = declarations.iter().find(|declaration| {
156            declaration.id == target && declaration.provides.iter().any(|item| item == capability)
157        }) {
158            return Some(declaration);
159        }
160        let parent = declarations
161            .iter()
162            .find(|declaration| declaration.id == target)
163            .and_then(|declaration| declaration.parent)?;
164        if parent == target {
165            return None;
166        }
167        target = parent;
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::{CapabilityDeclaration, CapabilityRequirement, missing_capabilities};
174    use crate::registry_core::identity::{NodeId, ROOT_NODE_ID};
175
176    fn id(byte: u8) -> NodeId {
177        NodeId::from_raw([byte, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
178    }
179
180    fn declaration(
181        byte: u8,
182        parent: Option<u8>,
183        kind: &str,
184        provides: &[&str],
185    ) -> CapabilityDeclaration {
186        CapabilityDeclaration {
187            id: id(byte),
188            parent: parent.map(id),
189            kind: kind.to_owned(),
190            provides: provides.iter().map(|item| item.to_string()).collect(),
191        }
192    }
193
194    fn requirement(
195        byte: u8,
196        parent: Option<u8>,
197        capability: &str,
198        provider: &str,
199    ) -> CapabilityRequirement {
200        CapabilityRequirement {
201            node: id(byte),
202            kind: "Leaf".to_owned(),
203            function: "register".to_owned(),
204            branch: "a".to_owned(),
205            capability: capability.to_owned(),
206            provider: provider.to_owned(),
207            parent: parent.map(id),
208            source: "a/leaf.rs".to_owned(),
209            line: 3,
210        }
211    }
212
213    #[test]
214    fn satisfied_requirement_produces_no_diagnostic() {
215        let declarations = vec![
216            declaration(1, None, "CanvasProvider", &["render"]),
217            declaration(2, Some(1), "Leaf", &[]),
218        ];
219        let requirements = vec![requirement(2, Some(1), "render", "CanvasProvider")];
220        assert!(missing_capabilities(requirements, &declarations).is_empty());
221    }
222
223    #[test]
224    fn missing_capability_is_reported_with_ancestor_detail() {
225        let declarations = vec![
226            declaration(1, None, "WrongProvider", &["render"]),
227            declaration(2, Some(1), "Leaf", &[]),
228        ];
229        let requirements = vec![requirement(2, Some(1), "render", "CanvasProvider")];
230        let missing = missing_capabilities(requirements, &declarations);
231        assert_eq!(missing.len(), 1);
232        let rendered = missing[0].clone().message;
233        assert!(rendered.contains("missing capability `render`"));
234    }
235
236    #[test]
237    fn root_requirement_without_parent_is_reported() {
238        let declarations = vec![declaration(1, None, "Root", &[])];
239        let requirements = vec![requirement(1, None, "render", "CanvasProvider")];
240        assert_eq!(missing_capabilities(requirements, &declarations).len(), 1);
241    }
242
243    #[test]
244    fn parent_cycle_does_not_hang() {
245        let mut root = declaration(1, Some(2), "A", &[]);
246        root.parent = Some(id(2));
247        let mut other = declaration(2, Some(1), "B", &[]);
248        other.parent = Some(id(1));
249        let declarations = vec![root, other];
250        let requirements = vec![requirement(2, Some(1), "render", "CanvasProvider")];
251        let missing = missing_capabilities(requirements, &declarations);
252        assert_eq!(missing.len(), 1);
253        let _ = ROOT_NODE_ID;
254    }
255}