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)]
9pub struct MayaCommandRegistry;
11
12impl MayaCommandRegistry {
13 #[must_use]
14 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}