Skip to main content

workshop_rs/settings/
mod.rs

1//! The neutral settings carrier.
2//!
3//! A typed, non-serde tree for custom-game-settings blocks shared by
4//! validation and emission. The tree is a carrier: settings are carried and
5//! emitted, never interpreted by lowering/analysis. Source-backed path and
6//! display data remain implementation details behind this module's APIs.
7//!
8//! Extracted from the Wright-authored `wright-ir` crate; see
9//! [`docs/provenance.md`](https://github.com/wrightkit/workshop-rs/blob/main/docs/provenance.md).
10
11pub(crate) mod check;
12pub(crate) mod emitter;
13mod member;
14pub(crate) mod parser;
15
16pub(crate) mod reconciliation;
17pub(crate) mod schema;
18pub(crate) mod table;
19
20/// A segment of a path accepted by settings schema lookups.
21#[derive(Debug, Clone, Copy, Hash)]
22pub enum PathPart<'a> {
23    /// A literal key (mode names under `gamemodes` are literal keys too:
24    /// per-key subsets are exact-path entries).
25    Part(&'a str),
26    /// Any team slot (allTeams).
27    Team,
28    /// Any hero-config slot.
29    Hero,
30}
31
32impl<'b> PartialEq<PathPart<'b>> for PathPart<'_> {
33    fn eq(&self, other: &PathPart<'b>) -> bool {
34        match (self, other) {
35            (PathPart::Part(left), PathPart::Part(right)) => left == right,
36            (PathPart::Team, PathPart::Team) => true,
37            (PathPart::Hero, PathPart::Hero) => true,
38            _ => false,
39        }
40    }
41}
42
43impl Eq for PathPart<'_> {}
44
45pub use check::{
46    DiagnosticSeverity, SettingsDiagnostic, check_emission, check_emission_diagnostics,
47};
48pub use schema::{
49    Applicability, EffectiveNumber, NumericBounds, NumericBoundsError, SettingDefinition,
50    SettingEnumMember, SettingId, SettingIdentity, SettingOccurrence, SettingOperationError,
51    SettingPresentation, SettingScope, SettingSource, SettingSourceEdit, SettingSourceKind,
52    SettingTarget, SettingTargetKind, SettingValue, SettingValueDomain, TeamId, definition,
53    definitions, definitions_by_id, validate_catalog,
54};
55
56use crate::core::source::Span;
57
58/// A settings block: `settings { ... }` with its typed children.
59#[derive(Debug, Clone)]
60pub struct Settings {
61    pub span: Option<Span>,
62    pub children: Vec<SettingsNode>,
63}
64
65/// One member of a settings group.
66#[derive(Debug, Clone)]
67pub enum SettingsNode {
68    /// User-authored mode data under `settings.workshop`.
69    Workshop {
70        children: Vec<SettingsNode>,
71        span: Option<Span>,
72    },
73    Group {
74        name: String,
75        children: Vec<SettingsNode>,
76        span: Option<Span>,
77    },
78    Number {
79        name: String,
80        value: f64,
81        span: Option<Span>,
82    },
83    Bool {
84        name: String,
85        value: bool,
86        span: Option<Span>,
87    },
88    /// A presence-only Workshop extension setting (for example `Beam Effects`).
89    Flag { name: String, span: Option<Span> },
90    String {
91        name: String,
92        value: String,
93        span: Option<Span>,
94    },
95    List {
96        name: String,
97        elements: Vec<SettingsListElement>,
98        span: Option<Span>,
99    },
100    /// A syntactically valid settings member whose semantic catalog entry is
101    /// not yet declared. The raw value is carried explicitly so parsing does
102    /// not fabricate a type or silently discard project settings.
103    Raw {
104        name: String,
105        value: String,
106        span: Option<Span>,
107    },
108    /// A member written as `name: value` with the value as authored and never
109    /// interpreted. At a catalogued path `name` is the canonical key and is
110    /// emitted in the target locale; elsewhere it is written as is. Carries
111    /// catalogued keys with values the catalog does not declare, and members
112    /// whose empty value still needs its `:`.
113    RawValue {
114        name: String,
115        value: String,
116        span: Option<Span>,
117    },
118}
119
120/// One element of a settings list (corpus lists are all strings).
121#[derive(Debug, Clone)]
122pub struct SettingsListElement {
123    pub value: String,
124    pub span: Option<Span>,
125}
126
127impl SettingsListElement {
128    fn clear_span(&mut self) {
129        self.span = None;
130    }
131}
132
133impl Settings {
134    /// Drop every span recorded on the block and its members.
135    ///
136    /// Called when the program's file table is rebound to a different
137    /// coordinate space (`SourceMap::apply`): the map carries no settings
138    /// entries, so the spans parsed against the previous text cannot be
139    /// re-anchored and must not survive as stale positions.
140    pub(crate) fn clear_spans(&mut self) {
141        self.span = None;
142        for node in &mut self.children {
143            node.clear_spans();
144        }
145    }
146}
147
148impl SettingsNode {
149    /// The source span of this node, if any.
150    pub fn span(&self) -> Option<Span> {
151        match self {
152            SettingsNode::Workshop { span, .. }
153            | SettingsNode::Group { span, .. }
154            | SettingsNode::Number { span, .. }
155            | SettingsNode::Bool { span, .. }
156            | SettingsNode::Flag { span, .. }
157            | SettingsNode::String { span, .. }
158            | SettingsNode::List { span, .. }
159            | SettingsNode::Raw { span, .. }
160            | SettingsNode::RawValue { span, .. } => *span,
161        }
162    }
163
164    /// Drop this node's span and, for containers, every descendant span.
165    fn clear_spans(&mut self) {
166        match self {
167            SettingsNode::Workshop { children, span }
168            | SettingsNode::Group { children, span, .. } => {
169                *span = None;
170                for node in children {
171                    node.clear_spans();
172                }
173            }
174            SettingsNode::Number { span, .. }
175            | SettingsNode::Bool { span, .. }
176            | SettingsNode::Flag { span, .. }
177            | SettingsNode::String { span, .. }
178            | SettingsNode::Raw { span, .. }
179            | SettingsNode::RawValue { span, .. } => *span = None,
180            SettingsNode::List { elements, span, .. } => {
181                *span = None;
182                for element in elements {
183                    element.clear_span();
184                }
185            }
186        }
187    }
188
189    /// The key name of this node.
190    pub fn name(&self) -> &str {
191        match self {
192            SettingsNode::Workshop { .. } => "workshop",
193            SettingsNode::Group { name, .. }
194            | SettingsNode::Number { name, .. }
195            | SettingsNode::Bool { name, .. }
196            | SettingsNode::Flag { name, .. }
197            | SettingsNode::String { name, .. }
198            | SettingsNode::List { name, .. }
199            | SettingsNode::Raw { name, .. }
200            | SettingsNode::RawValue { name, .. } => name,
201        }
202    }
203}