Skip to main content

maya_mel/maya/
registry.rs

1use mel_sema::{
2    CommandKind, CommandModeMask, CommandRegistry, CommandSchema, CommandSourceKind, FlagArity,
3    FlagArityByMode, FlagSchema, PositionalSchema, PositionalSlotSchema, PositionalSourcePolicy,
4    PositionalTailSchema, ReturnBehavior, ValidatedCommandSchema, ValueShape,
5};
6use std::sync::OnceLock;
7
8#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
9/// Builtin Maya command registry embedded in the crate.
10pub struct MayaCommandRegistry;
11
12impl MayaCommandRegistry {
13    #[must_use]
14    /// Construct the builtin Maya command registry.
15    pub const fn new() -> Self {
16        Self
17    }
18}
19
20impl CommandRegistry for MayaCommandRegistry {
21    fn lookup(&self, name: &str) -> Option<&ValidatedCommandSchema> {
22        shared_command_schemas()
23            .binary_search_by(|schema| schema.name.as_ref().cmp(name))
24            .ok()
25            .map(|index| &shared_command_schemas()[index])
26    }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30struct EmbeddedFlagSchema {
31    long_name: &'static str,
32    short_name: Option<&'static str>,
33    mode_mask: CommandModeMask,
34    arity_by_mode: FlagArityByMode,
35    value_shapes: &'static [ValueShape],
36    allows_multiple: bool,
37}
38
39impl EmbeddedFlagSchema {
40    fn to_shared_schema(self) -> FlagSchema {
41        FlagSchema {
42            long_name: self.long_name.into(),
43            short_name: self.short_name.map(Into::into),
44            mode_mask: self.mode_mask,
45            arity_by_mode: self.arity_by_mode,
46            value_shapes: self.value_shapes.into(),
47            allows_multiple: self.allows_multiple,
48        }
49    }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53struct EmbeddedCommandSchema {
54    name: &'static str,
55    kind: CommandKind,
56    source_kind: CommandSourceKind,
57    mode_mask: CommandModeMask,
58    return_behavior: ReturnBehavior,
59    positionals: PositionalSchema,
60    flags: &'static [EmbeddedFlagSchema],
61}
62
63impl EmbeddedCommandSchema {
64    fn to_shared_schema(self) -> ValidatedCommandSchema {
65        let schema = CommandSchema {
66            name: self.name.into(),
67            kind: self.kind,
68            source_kind: self.source_kind,
69            mode_mask: self.mode_mask,
70            return_behavior: self.return_behavior,
71            positionals: self.positionals,
72            flags: self.build_effective_flags().into(),
73        };
74        ValidatedCommandSchema::new(schema)
75            .expect("embedded command schemas must satisfy mel-sema schema invariants")
76    }
77
78    fn build_effective_flags(self) -> Vec<FlagSchema> {
79        let mut flags: Vec<FlagSchema> = self
80            .flags
81            .iter()
82            .copied()
83            .map(EmbeddedFlagSchema::to_shared_schema)
84            .collect();
85        push_synthetic_mode_flag(&mut flags, self.mode_mask.create, "create", "c");
86        push_synthetic_mode_flag(&mut flags, self.mode_mask.edit, "edit", "e");
87        push_synthetic_mode_flag(&mut flags, self.mode_mask.query, "query", "q");
88        flags
89    }
90}
91
92static EMBEDDED_COMMAND_SCHEMAS: &[EmbeddedCommandSchema] =
93    include!(concat!(env!("OUT_DIR"), "/embedded_command_schemas.rs"));
94
95fn shared_command_schemas() -> &'static [ValidatedCommandSchema] {
96    static COMMAND_SCHEMAS: OnceLock<Vec<ValidatedCommandSchema>> = OnceLock::new();
97    COMMAND_SCHEMAS.get_or_init(|| {
98        EMBEDDED_COMMAND_SCHEMAS
99            .iter()
100            .copied()
101            .map(EmbeddedCommandSchema::to_shared_schema)
102            .collect()
103    })
104}
105
106pub(crate) fn push_synthetic_mode_flag(
107    flags: &mut Vec<FlagSchema>,
108    enabled: bool,
109    long_name: &str,
110    short_name: &str,
111) {
112    if !enabled
113        || flags.iter().any(|flag| {
114            flag.long_name.as_ref() == long_name || flag.short_name.as_deref() == Some(short_name)
115        })
116    {
117        return;
118    }
119
120    flags.push(FlagSchema {
121        long_name: long_name.into(),
122        short_name: Some(short_name.into()),
123        mode_mask: CommandModeMask {
124            create: true,
125            edit: true,
126            query: true,
127        },
128        arity_by_mode: FlagArityByMode {
129            create: FlagArity::None,
130            edit: FlagArity::None,
131            query: FlagArity::None,
132        },
133        value_shapes: Vec::new().into(),
134        allows_multiple: false,
135    });
136}
137
138pub(crate) struct OverlayRegistry<'a, R: ?Sized> {
139    primary: &'a R,
140    fallback: MayaCommandRegistry,
141}
142
143impl<'a, R> OverlayRegistry<'a, R>
144where
145    R: CommandRegistry + ?Sized,
146{
147    pub(crate) const fn new(primary: &'a R) -> Self {
148        Self {
149            primary,
150            fallback: MayaCommandRegistry::new(),
151        }
152    }
153}
154
155impl<R> CommandRegistry for OverlayRegistry<'_, R>
156where
157    R: CommandRegistry + ?Sized,
158{
159    fn lookup(&self, name: &str) -> Option<&ValidatedCommandSchema> {
160        self.primary
161            .lookup(name)
162            .or_else(|| self.fallback.lookup(name))
163    }
164}