workshop_rs/core/signatures.rs
1//! Canonical signature context for ambiguous enum member resolution.
2//!
3//! The canonical catalog pins the expected enum domain of selected call
4//! argument positions (e.g. `createHudText` argument 9 is `HudReeval`). The
5//! Workshop parse path uses those expected domains to resolve a bare enum
6//! member spelling that is ambiguous across domains (e.g. the shared `None`
7//! member of `ChaseTimeReeval` / `ChaseRateReeval` / `Invis`).
8//!
9//! This module defines the minimal parse-context contract between the
10//! catalog owner and the Workshop frontend. It deliberately carries no
11//! signature data: the catalog data file remains the only domain table.
12//! (Extracted from the Wright-authored `wright_core::signatures` module;
13//! see [`docs/provenance.md`](https://github.com/wrightkit/workshop-rs/blob/main/docs/provenance.md).)
14
15/// Supplies the expected enum domain for a call argument during parsing.
16///
17/// The parser asks for the expected domain of argument `arg_index` (0-based)
18/// of the call whose Workshop catalog id is `catalog_id`. Implementations
19/// must return the domain only when the canonical signature pins exactly one;
20/// returning `None` leaves an ambiguous bare member unresolved.
21pub trait ExpectedDomain {
22 /// The expected enum domain for `arg_index` of the call with catalog id
23 /// `catalog_id`, or `None` when the signature does not pin one.
24 fn expected_domain(&self, catalog_id: &str, arg_index: usize) -> Option<&str>;
25}
26
27/// A context with no signature metadata. Ambiguous bare enum members remain
28/// structured with their catalog candidates. Used by callers that intentionally
29/// need context-free parsing.
30#[derive(Debug, Clone, Copy, Default)]
31pub struct NoExpectedDomain;
32
33impl ExpectedDomain for NoExpectedDomain {
34 fn expected_domain(&self, _catalog_id: &str, _arg_index: usize) -> Option<&str> {
35 None
36 }
37}
38
39/// A context chain: consult `first`, then fall back to `second`.
40///
41/// Callers that combine signature sources (e.g. a provider manifest followed
42/// by the canonical catalog) chain them; neither is authoritative alone.
43#[derive(Clone, Copy)]
44pub struct ChainedExpectedDomain<'a, 'b> {
45 first: &'a dyn ExpectedDomain,
46 second: &'b dyn ExpectedDomain,
47}
48
49impl<'a, 'b> ChainedExpectedDomain<'a, 'b> {
50 /// Chain two contexts, consulting `first` before `second`.
51 pub fn new(first: &'a dyn ExpectedDomain, second: &'b dyn ExpectedDomain) -> Self {
52 ChainedExpectedDomain { first, second }
53 }
54}
55
56impl ExpectedDomain for ChainedExpectedDomain<'_, '_> {
57 fn expected_domain(&self, catalog_id: &str, arg_index: usize) -> Option<&str> {
58 self.first
59 .expected_domain(catalog_id, arg_index)
60 .or_else(|| self.second.expected_domain(catalog_id, arg_index))
61 }
62}