Skip to main content

nichlink/registry_core/syntax/
fields.rs

1//! Field-value decoding for parsed registration faces.
2//! 已解析注册面的字段取值解码。
3//!
4//! [`FaceSyntax`] stores each field's raw tokens; this layer turns one field's
5//! tokens into the typed value a caller asked for — path, string, boolean, list,
6//! requirement pair, optional string, parent — and builds the field map from a
7//! face body in the first place.
8//! [`FaceSyntax`] 保存每个字段的原始 token;本层把一个字段的 token 变成调用方索取的
9//! 有类型值——路径、字符串、布尔、列表、需求对、可选字符串、父级——并负责从注册面
10//! 主体构建字段映射。
11
12use std::collections::BTreeMap;
13
14use proc_macro2::{Delimiter, Span, TokenStream, TokenTree};
15
16use super::tokens::{literal_string, location, only_group, path_to_string, split_top_level};
17use super::{
18    FaceSyntax, FaceSyntaxError, FieldSyntax, ParentSyntax, SyntaxLocation, compact,
19    split_face_fields,
20};
21
22impl FaceSyntax {
23    /// One field's tokens as compact source text, exactly as written.
24    /// 某个字段的 token 渲染成的紧凑源码文本,按原文输出。
25    ///
26    /// This is the escape hatch for fields the typed readers below do not cover:
27    /// it answers `None` only when the field is absent, never because of its shape.
28    /// 这是下面那些有类型读取器未覆盖字段的出口:只有字段缺失时才返回 `None`,绝不因形状
29    /// 而返回。
30    pub fn field(&self, name: &str) -> Option<String> {
31        self.fields.get(name).map(|field| compact(&field.tokens))
32    }
33
34    /// The `cfg` attribute written on the declaration, if any.
35    /// 声明上写下的 `cfg` 属性(若有)。
36    pub fn cfg(&self) -> Option<&str> {
37        self.cfg.as_deref()
38    }
39
40    /// Where one field was written, so a diagnostic can point at it.
41    /// 某个字段写在哪,供诊断指向它。
42    pub fn field_location(&self, name: &str) -> Option<&SyntaxLocation> {
43        self.fields.get(name).map(|field| &field.location)
44    }
45
46    /// One field as a Rust path; `None` when it is absent or is not a path.
47    /// 某个字段作为 Rust 路径;字段缺失或不是路径时为 `None`。
48    pub fn path(&self, name: &str) -> Option<String> {
49        let field = self.fields.get(name)?;
50        syn::parse2::<syn::Path>(field.tokens.clone())
51            .ok()
52            .map(|path| path_to_string(&path))
53    }
54
55    /// One field as a string literal; `None` when it is absent or is not one.
56    /// 某个字段作为字符串字面量;字段缺失或不是字面量时为 `None`。
57    pub fn string(&self, name: &str) -> Option<String> {
58        let field = self.fields.get(name)?;
59        syn::parse2::<syn::LitStr>(field.tokens.clone())
60            .ok()
61            .map(|literal| literal.value())
62    }
63
64    /// One field as a boolean literal; `None` when it is absent or is not one.
65    /// 某个字段作为布尔字面量;字段缺失或不是字面量时为 `None`。
66    pub fn boolean(&self, name: &str) -> Option<bool> {
67        let field = self.fields.get(name)?;
68        syn::parse2::<syn::LitBool>(field.tokens.clone())
69            .ok()
70            .map(|literal| literal.value)
71    }
72
73    /// One localized field's text in one language, as written
74    /// (`name: { zh: "…", en: "…" }`).
75    /// 某个本地化字段在指定语言下的文本,按原文(`name: { zh: "…", en: "…" }`)。
76    pub fn localized(&self, name: &str, language: &str) -> Option<String> {
77        let field = self.fields.get(name)?;
78        let group = only_group(&field.tokens, Delimiter::Brace)?;
79        parse_fields(group.stream(), group.span())
80            .ok()?
81            .get(language)
82            .and_then(|field| syn::parse2::<syn::LitStr>(field.tokens.clone()).ok())
83            .map(|literal| literal.value())
84    }
85
86    /// One bracketed list field as strings, in the written order.
87    /// 某个方括号列表字段的字符串,按书写顺序。
88    ///
89    /// Two names answer a *derived* value rather than the literal one:
90    /// `handle_traits` and `part_traits` are the searchable labels of the
91    /// compiler-checked paths in `handle_contracts` and `part_contracts`. A face
92    /// that states a path therefore does not state the label a second time, and
93    /// every reader of a parsed face — the build-time contract check, the
94    /// authoring parser, Studio — agrees by construction instead of by
95    /// convention. A face that states only a label keeps it: that is the
96    /// unchecked claim it always was.
97    /// 有两个名字返回的是**推导**值而不是字面值:`handle_traits` 与 `part_traits` 是
98    /// `handle_contracts` 与 `part_contracts` 里那些参与编译检查的路径的可检索标签。
99    /// 因此写了路径的注册面不必再写一遍标签,而每个读取已解析注册面的地方——构建期合同
100    /// 检查、创作解析器、Studio——是构造上一致,而不是靠约定一致。只写标签的注册面则保留
101    /// 标签:它本来就是一条未经检查的声明。
102    pub fn string_list(&self, name: &str) -> Option<Vec<String>> {
103        match name {
104            "handle_traits" => return self.trait_labels("handle_contracts", "handle_traits"),
105            "part_traits" => return self.trait_labels("part_contracts", "part_traits"),
106            _ => {}
107        }
108        self.written_string_list(name)
109    }
110
111    /// The labels a bracketed trait-path list names, or the labels written beside
112    /// it when it names nothing.
113    /// 某个方括号 trait 路径列表所命名的标签;它一个路径都没有时,用它旁边写下的标签。
114    fn trait_labels(&self, path_field: &str, label_field: &str) -> Option<Vec<String>> {
115        let paths = self.path_list(path_field).unwrap_or_default();
116        if paths.is_empty() {
117            return self.written_string_list(label_field);
118        }
119        crate::authoring::parse::trait_names_from_paths(&paths.join(","))
120            .ok()
121            .map(|labels| {
122                labels
123                    .split(',')
124                    .map(str::trim)
125                    .filter(|label| !label.is_empty())
126                    .map(str::to_owned)
127                    .collect()
128            })
129    }
130
131    /// One bracketed list field exactly as written, with no derivation.
132    /// 某个方括号列表字段按原文读出,不做任何推导。
133    fn written_string_list(&self, name: &str) -> Option<Vec<String>> {
134        let field = self.fields.get(name)?;
135        let group = only_group(&field.tokens, Delimiter::Bracket)?;
136        split_top_level(group.stream())
137            .into_iter()
138            .map(|tokens| syn::parse2::<syn::LitStr>(tokens).map(|literal| literal.value()))
139            .collect::<Result<Vec<_>, _>>()
140            .ok()
141    }
142
143    /// One bracketed list field as Rust paths, in the written order.
144    /// 某个方括号列表字段的 Rust 路径,按书写顺序。
145    pub fn path_list(&self, name: &str) -> Option<Vec<String>> {
146        let field = self.fields.get(name)?;
147        let group = only_group(&field.tokens, Delimiter::Bracket)?;
148        split_top_level(group.stream())
149            .into_iter()
150            .map(|tokens| syn::parse2::<syn::Path>(tokens).map(|path| path_to_string(&path)))
151            .collect::<Result<Vec<_>, _>>()
152            .ok()
153    }
154
155    /// One list field as `capability => provider` pairs.
156    /// 某个列表字段的 `capability => provider` 对。
157    pub fn requirements(&self, name: &str) -> Option<Vec<(String, String)>> {
158        let field = self.fields.get(name)?;
159        let group = only_group(&field.tokens, Delimiter::Bracket)?;
160        split_top_level(group.stream())
161            .into_iter()
162            .map(parse_requirement)
163            .collect::<Option<Vec<_>>>()
164    }
165
166    /// One field as a tri-state string: `Some(Some(text))` for `Some("text")`,
167    /// `Some(None)` for `None`, and `None` when the field is absent or shaped
168    /// differently.
169    /// 某个字段的三态字符串:`Some("text")` 得 `Some(Some(text))`,`None` 得
170    /// `Some(None)`,字段缺失或形状不同得 `None`。
171    ///
172    /// The outer `Option` separates "not written" from "written as `None`", which
173    /// an edit must not collapse: one means leave the field alone.
174    /// 外层 `Option` 把"没写"与"写成 `None`"分开,编辑时不能合并:前者意味着不要动这个字段。
175    pub fn option_string(&self, name: &str) -> Option<Option<String>> {
176        let field = self.fields.get(name)?;
177        let expression = syn::parse2::<syn::Expr>(field.tokens.clone()).ok()?;
178        match expression {
179            syn::Expr::Path(path) if path.path.is_ident("None") => Some(None),
180            syn::Expr::Call(call) => {
181                let syn::Expr::Path(function) = *call.func else {
182                    return None;
183                };
184                if !function.path.is_ident("Some") || call.args.len() != 1 {
185                    return None;
186                }
187                let syn::Expr::Lit(argument) = call.args.first()? else {
188                    return None;
189                };
190                let syn::Lit::Str(value) = &argument.lit else {
191                    return None;
192                };
193                Some(Some(value.value()))
194            }
195            _ => None,
196        }
197    }
198
199    /// The declared parent, classified as far as the text allows; `None` when the
200    /// field is absent or its expression is not one of the three known shapes.
201    /// 声明的父级,按文本能分类到的程度给出;字段缺失或表达式不属于三种已知形状时为
202    /// `None`。
203    pub fn parent(&self) -> Option<ParentSyntax> {
204        let field = self.fields.get("parent")?;
205        let expression = syn::parse2::<syn::Expr>(field.tokens.clone()).ok()?;
206        match expression {
207            syn::Expr::Path(path) => {
208                let path = path_to_string(&path.path);
209                if path.ends_with("ROOT_NODE_ID") {
210                    Some(ParentSyntax::Root)
211                } else {
212                    path.strip_suffix("::NODE_ID")
213                        .map(|module| ParentSyntax::NodePath(module.to_owned()))
214                }
215            }
216            syn::Expr::Call(call) => parse_parent_call(call),
217            _ => None,
218        }
219    }
220}
221
222/// Build the field map of one face body, rejecting a malformed `name: value`.
223/// 构建一个注册面主体的字段映射,拒绝格式错误的 `name: value`。
224pub(super) fn parse_fields(
225    tokens: TokenStream,
226    fallback_span: Span,
227) -> Result<BTreeMap<String, FieldSyntax>, FaceSyntaxError> {
228    let mut fields = BTreeMap::new();
229    for field in split_face_fields(tokens) {
230        let mut tokens = field.into_iter();
231        let Some(TokenTree::Ident(name)) = tokens.next() else {
232            return Err(super::syntax_error(
233                fallback_span,
234                "expected a registration field name",
235            ));
236        };
237        let Some(TokenTree::Punct(colon)) = tokens.next() else {
238            return Err(super::syntax_error(
239                name.span(),
240                format!("expected `:` after `{name}`"),
241            ));
242        };
243        if colon.as_char() != ':' {
244            return Err(super::syntax_error(
245                colon.span(),
246                format!("expected `:` after `{name}`"),
247            ));
248        }
249        let value = tokens.collect::<TokenStream>();
250        if value.is_empty() {
251            return Err(super::syntax_error(
252                name.span(),
253                format!("field `{name}` has no value"),
254            ));
255        }
256        let field_name = name.to_string();
257        if fields
258            .insert(
259                field_name.clone(),
260                FieldSyntax {
261                    tokens: value,
262                    location: location(name.span()),
263                },
264            )
265            .is_some()
266        {
267            return Err(super::syntax_error(
268                name.span(),
269                format!("duplicate field `{field_name}`"),
270            ));
271        }
272    }
273    Ok(fields)
274}
275
276/// Read one `"capability" => "provider"` requirement pair.
277/// 读取一条 `"capability" => "provider"` 需求对。
278fn parse_requirement(tokens: TokenStream) -> Option<(String, String)> {
279    let tokens = tokens.into_iter().collect::<Vec<_>>();
280    let arrow = tokens.windows(2).position(|pair| {
281        matches!(&pair[0], TokenTree::Punct(punct) if punct.as_char() == '=')
282            && matches!(&pair[1], TokenTree::Punct(punct) if punct.as_char() == '>')
283    })?;
284    let left = tokens[..arrow].iter().cloned().collect::<TokenStream>();
285    let right = tokens[arrow + 2..].iter().cloned().collect::<TokenStream>();
286    let capability = syn::parse2::<syn::LitStr>(left).ok()?.value();
287    let provider = syn::parse2::<syn::LitStr>(right).ok()?.value();
288    Some((capability, provider))
289}
290
291/// Read the `parent:` constructor call forms the authoring layer writes.
292/// 读取创作层写下的 `parent:` 构造函数形式。
293fn parse_parent_call(call: syn::ExprCall) -> Option<ParentSyntax> {
294    let syn::Expr::Path(function) = *call.func else {
295        return None;
296    };
297    let function = path_to_string(&function.path);
298    if function.ends_with("root_node_id") && call.args.len() == 1 {
299        return Some(ParentSyntax::Root);
300    }
301    if !function.ends_with("NodeId::from_path") || call.args.len() != 2 {
302        return None;
303    }
304    let mut arguments = call.args.iter();
305    let source = literal_string(arguments.next()?)?;
306    let kind = literal_string(arguments.next()?)?;
307    Some(ParentSyntax::FromPath { source, kind })
308}