meta_language/concept_records.rs
1//! The concept records shared with the JavaScript runtime.
2//!
3//! `src/data/concept-records.json` is generated from
4//! `parity/naming/canonical-concepts.json` by
5//! `js/scripts/build-concept-records.mjs`; the npm package ships the same
6//! file, so both runtimes agree on every canonical identity, its readable
7//! English phrase, definition, constraints, source aliases and former names.
8
9use std::sync::OnceLock;
10
11use serde::Deserialize;
12
13const CONCEPT_RECORDS_JSON: &str = include_str!("data/concept-records.json");
14
15/// Whether a canonical name is a concept (a noun phrase) or an operation (a verb phrase).
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
17#[serde(rename_all = "lowercase")]
18pub enum ConceptRole {
19 /// A concept, named by a noun phrase.
20 Concept,
21 /// An operation or relation, named by a verb phrase.
22 Operation,
23}
24
25/// The name a source (a grammar format, a programming language, a surface) gives a concept.
26#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
27pub struct SourceAlias {
28 /// The source, such as `ebnf`, `Rust` or `Russian grammar surface`.
29 pub source: String,
30 /// The name or notation the source uses for the concept.
31 pub name: String,
32}
33
34/// A concept whose phrase is a synonym of another's, with what distinguishes them.
35#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
36pub struct ConceptDistinction {
37 /// The identity of the synonymous concept.
38 pub id: String,
39 /// What distinguishes the two concepts.
40 pub reason: String,
41}
42
43/// The record of one canonical concept or operation.
44#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
45#[serde(rename_all = "camelCase", deny_unknown_fields)]
46pub struct ConceptRecord {
47 /// Stable identity, such as `grammar.character-class`.
48 pub id: String,
49 /// The readable English phrase the identity spells, such as `character class`.
50 pub phrase: String,
51 /// Whether the phrase is a noun phrase or a verb phrase.
52 pub role: ConceptRole,
53 /// What the concept means.
54 pub definition: String,
55 /// The constraints the concept satisfies.
56 pub constraints: Vec<String>,
57 /// The names other sources give the concept.
58 pub source_aliases: Vec<SourceAlias>,
59 /// Former names that still decode to the concept.
60 pub former_names: Vec<String>,
61 /// The concept this one is a use of, such as the operation a stage performs.
62 #[serde(default)]
63 pub represents: Option<String>,
64 /// The concepts with a synonymous phrase this one is distinct from.
65 #[serde(default)]
66 pub distinct_from: Vec<ConceptDistinction>,
67}
68
69#[derive(Deserialize)]
70struct ConceptRecordFile {
71 concepts: Vec<ConceptRecord>,
72}
73
74/// Returns every concept record in register order.
75#[must_use]
76pub fn concept_records() -> &'static [ConceptRecord] {
77 static RECORDS: OnceLock<Vec<ConceptRecord>> = OnceLock::new();
78 RECORDS.get_or_init(|| {
79 serde_json::from_str::<ConceptRecordFile>(CONCEPT_RECORDS_JSON)
80 .expect("the embedded concept records are valid")
81 .concepts
82 })
83}
84
85/// Returns the record whose identity or former name is `name`.
86#[must_use]
87pub fn concept_record(name: &str) -> Option<&'static ConceptRecord> {
88 concept_records()
89 .iter()
90 .find(|record| record.id == name || record.former_names.iter().any(|former| former == name))
91}
92
93/// Returns the records a source (such as `ebnf` or `Rust`) names `name`, in register order.
94#[must_use]
95pub fn concept_records_for_source_name(source: &str, name: &str) -> Vec<&'static ConceptRecord> {
96 concept_records()
97 .iter()
98 .filter(|record| {
99 record
100 .source_aliases
101 .iter()
102 .any(|alias| alias.source == source && alias.name == name)
103 })
104 .collect()
105}