use crate::commands::{CommandName, Input};
pub mod abstract_action;
pub mod add_action;
pub mod build_action;
pub mod build_executor;
pub mod generate_action;
pub mod info_action;
pub mod new_action;
pub mod start_action;
pub mod start_executor;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ActionKind {
Add,
Build,
Generate,
Info,
New,
Start,
}
impl ActionKind {
pub const fn as_str(self) -> &'static str {
match self {
Self::Add => "add",
Self::Build => "build",
Self::Generate => "generate",
Self::Info => "info",
Self::New => "new",
Self::Start => "start",
}
}
pub const fn command(self) -> CommandName {
match self {
Self::Add => CommandName::Add,
Self::Build => CommandName::Build,
Self::Generate => CommandName::Generate,
Self::Info => CommandName::Info,
Self::New => CommandName::New,
Self::Start => CommandName::Start,
}
}
}
impl From<CommandName> for ActionKind {
fn from(command: CommandName) -> Self {
match command {
CommandName::Add => Self::Add,
CommandName::Build => Self::Build,
CommandName::Generate => Self::Generate,
CommandName::Info => Self::Info,
CommandName::New => Self::New,
CommandName::Start => Self::Start,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ActionEffect {
ExecuteSchematic,
InstallPackage,
RunBuild,
SpawnApplication,
InitializeGitRepository,
WriteGitIgnore,
DisplayProjectInformation,
PromptForMissingInput,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ActionSpec {
pub kind: ActionKind,
pub upstream_class: &'static str,
pub effects: &'static [ActionEffect],
pub accepts_extra_flags: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ActionInvocation {
pub kind: ActionKind,
pub inputs: Vec<Input>,
pub options: Vec<Input>,
pub extra_flags: Vec<String>,
}
impl ActionInvocation {
pub fn new(kind: ActionKind) -> Self {
Self {
kind,
inputs: Vec::new(),
options: Vec::new(),
extra_flags: Vec::new(),
}
}
pub fn for_command(command: CommandName) -> Self {
Self::new(command.into())
}
}
const ADD_EFFECTS: &[ActionEffect] = &[
ActionEffect::InstallPackage,
ActionEffect::PromptForMissingInput,
ActionEffect::ExecuteSchematic,
];
const BUILD_EFFECTS: &[ActionEffect] = &[ActionEffect::RunBuild];
const GENERATE_EFFECTS: &[ActionEffect] = &[
ActionEffect::PromptForMissingInput,
ActionEffect::ExecuteSchematic,
];
const INFO_EFFECTS: &[ActionEffect] = &[ActionEffect::DisplayProjectInformation];
const NEW_EFFECTS: &[ActionEffect] = &[
ActionEffect::PromptForMissingInput,
ActionEffect::ExecuteSchematic,
ActionEffect::InstallPackage,
ActionEffect::InitializeGitRepository,
ActionEffect::WriteGitIgnore,
];
const START_EFFECTS: &[ActionEffect] = &[ActionEffect::RunBuild, ActionEffect::SpawnApplication];
pub const ACTION_SPECS: &[ActionSpec] = &[
ActionSpec {
kind: ActionKind::Add,
upstream_class: "AddAction",
effects: ADD_EFFECTS,
accepts_extra_flags: true,
},
ActionSpec {
kind: ActionKind::Build,
upstream_class: "BuildAction",
effects: BUILD_EFFECTS,
accepts_extra_flags: false,
},
ActionSpec {
kind: ActionKind::Generate,
upstream_class: "GenerateAction",
effects: GENERATE_EFFECTS,
accepts_extra_flags: false,
},
ActionSpec {
kind: ActionKind::Info,
upstream_class: "InfoAction",
effects: INFO_EFFECTS,
accepts_extra_flags: false,
},
ActionSpec {
kind: ActionKind::New,
upstream_class: "NewAction",
effects: NEW_EFFECTS,
accepts_extra_flags: false,
},
ActionSpec {
kind: ActionKind::Start,
upstream_class: "StartAction",
effects: START_EFFECTS,
accepts_extra_flags: true,
},
];
pub fn action_specs() -> &'static [ActionSpec] {
ACTION_SPECS
}
pub fn action_spec(kind: ActionKind) -> Option<&'static ActionSpec> {
ACTION_SPECS.iter().find(|action| action.kind == kind)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn action_kind_maps_to_command_name() {
assert_eq!(ActionKind::Generate.command(), CommandName::Generate);
assert_eq!(ActionKind::Start.command().as_str(), "start");
}
#[test]
fn captures_actions_that_accept_extra_flags() {
let actions_with_extra_flags: Vec<ActionKind> = action_specs()
.iter()
.filter(|action| action.accepts_extra_flags)
.map(|action| action.kind)
.collect();
assert_eq!(
actions_with_extra_flags,
[ActionKind::Add, ActionKind::Start]
);
}
#[test]
fn invocation_starts_empty_for_command() {
let invocation = ActionInvocation::for_command(CommandName::Build);
assert_eq!(invocation.kind, ActionKind::Build);
assert!(invocation.inputs.is_empty());
assert!(invocation.options.is_empty());
assert!(invocation.extra_flags.is_empty());
}
}