1use crate::commands::{CommandName, Input};
8
9pub mod abstract_action;
10pub mod add_action;
11pub mod build_action;
12pub mod build_executor;
13pub mod generate_action;
14pub mod info_action;
15pub mod new_action;
16pub mod start_action;
17pub mod start_executor;
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
21pub enum ActionKind {
22 Add,
23 Build,
24 Generate,
25 Info,
26 New,
27 Start,
28}
29
30impl ActionKind {
31 pub const fn as_str(self) -> &'static str {
32 match self {
33 Self::Add => "add",
34 Self::Build => "build",
35 Self::Generate => "generate",
36 Self::Info => "info",
37 Self::New => "new",
38 Self::Start => "start",
39 }
40 }
41
42 pub const fn command(self) -> CommandName {
43 match self {
44 Self::Add => CommandName::Add,
45 Self::Build => CommandName::Build,
46 Self::Generate => CommandName::Generate,
47 Self::Info => CommandName::Info,
48 Self::New => CommandName::New,
49 Self::Start => CommandName::Start,
50 }
51 }
52}
53
54impl From<CommandName> for ActionKind {
55 fn from(command: CommandName) -> Self {
56 match command {
57 CommandName::Add => Self::Add,
58 CommandName::Build => Self::Build,
59 CommandName::Generate => Self::Generate,
60 CommandName::Info => Self::Info,
61 CommandName::New => Self::New,
62 CommandName::Start => Self::Start,
63 }
64 }
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69pub enum ActionEffect {
70 ExecuteSchematic,
71 InstallPackage,
72 RunBuild,
73 SpawnApplication,
74 InitializeGitRepository,
75 WriteGitIgnore,
76 DisplayProjectInformation,
77 PromptForMissingInput,
78}
79
80#[derive(Clone, Copy, Debug, PartialEq, Eq)]
82pub struct ActionSpec {
83 pub kind: ActionKind,
84 pub upstream_class: &'static str,
85 pub effects: &'static [ActionEffect],
86 pub accepts_extra_flags: bool,
87}
88
89#[derive(Clone, Debug, PartialEq, Eq)]
91pub struct ActionInvocation {
92 pub kind: ActionKind,
93 pub inputs: Vec<Input>,
94 pub options: Vec<Input>,
95 pub extra_flags: Vec<String>,
96}
97
98impl ActionInvocation {
99 pub fn new(kind: ActionKind) -> Self {
100 Self {
101 kind,
102 inputs: Vec::new(),
103 options: Vec::new(),
104 extra_flags: Vec::new(),
105 }
106 }
107
108 pub fn for_command(command: CommandName) -> Self {
109 Self::new(command.into())
110 }
111}
112
113const ADD_EFFECTS: &[ActionEffect] = &[
114 ActionEffect::InstallPackage,
115 ActionEffect::PromptForMissingInput,
116 ActionEffect::ExecuteSchematic,
117];
118
119const BUILD_EFFECTS: &[ActionEffect] = &[ActionEffect::RunBuild];
120
121const GENERATE_EFFECTS: &[ActionEffect] = &[
122 ActionEffect::PromptForMissingInput,
123 ActionEffect::ExecuteSchematic,
124];
125
126const INFO_EFFECTS: &[ActionEffect] = &[ActionEffect::DisplayProjectInformation];
127
128const NEW_EFFECTS: &[ActionEffect] = &[
129 ActionEffect::PromptForMissingInput,
130 ActionEffect::ExecuteSchematic,
131 ActionEffect::InstallPackage,
132 ActionEffect::InitializeGitRepository,
133 ActionEffect::WriteGitIgnore,
134];
135
136const START_EFFECTS: &[ActionEffect] = &[ActionEffect::RunBuild, ActionEffect::SpawnApplication];
137
138pub const ACTION_SPECS: &[ActionSpec] = &[
139 ActionSpec {
140 kind: ActionKind::Add,
141 upstream_class: "AddAction",
142 effects: ADD_EFFECTS,
143 accepts_extra_flags: true,
144 },
145 ActionSpec {
146 kind: ActionKind::Build,
147 upstream_class: "BuildAction",
148 effects: BUILD_EFFECTS,
149 accepts_extra_flags: false,
150 },
151 ActionSpec {
152 kind: ActionKind::Generate,
153 upstream_class: "GenerateAction",
154 effects: GENERATE_EFFECTS,
155 accepts_extra_flags: false,
156 },
157 ActionSpec {
158 kind: ActionKind::Info,
159 upstream_class: "InfoAction",
160 effects: INFO_EFFECTS,
161 accepts_extra_flags: false,
162 },
163 ActionSpec {
164 kind: ActionKind::New,
165 upstream_class: "NewAction",
166 effects: NEW_EFFECTS,
167 accepts_extra_flags: false,
168 },
169 ActionSpec {
170 kind: ActionKind::Start,
171 upstream_class: "StartAction",
172 effects: START_EFFECTS,
173 accepts_extra_flags: true,
174 },
175];
176
177pub fn action_specs() -> &'static [ActionSpec] {
178 ACTION_SPECS
179}
180
181pub fn action_spec(kind: ActionKind) -> Option<&'static ActionSpec> {
182 ACTION_SPECS.iter().find(|action| action.kind == kind)
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn action_kind_maps_to_command_name() {
191 assert_eq!(ActionKind::Generate.command(), CommandName::Generate);
192 assert_eq!(ActionKind::Start.command().as_str(), "start");
193 }
194
195 #[test]
196 fn captures_actions_that_accept_extra_flags() {
197 let actions_with_extra_flags: Vec<ActionKind> = action_specs()
198 .iter()
199 .filter(|action| action.accepts_extra_flags)
200 .map(|action| action.kind)
201 .collect();
202
203 assert_eq!(
204 actions_with_extra_flags,
205 [ActionKind::Add, ActionKind::Start]
206 );
207 }
208
209 #[test]
210 fn invocation_starts_empty_for_command() {
211 let invocation = ActionInvocation::for_command(CommandName::Build);
212
213 assert_eq!(invocation.kind, ActionKind::Build);
214 assert!(invocation.inputs.is_empty());
215 assert!(invocation.options.is_empty());
216 assert!(invocation.extra_flags.is_empty());
217 }
218}