#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WorkflowStep {
Start,
SelectDirectory,
SelectListPattern,
ReviewFileList,
SelectRenamePattern,
ReviewPreview,
ConfigureApplyOptions,
ConfirmAndExecute,
ShowResults,
Complete,
}
impl WorkflowStep {
#[allow(dead_code)]
pub fn next(self) -> Option<Self> {
match self {
WorkflowStep::Start => Some(WorkflowStep::SelectDirectory),
WorkflowStep::SelectDirectory => Some(WorkflowStep::SelectListPattern),
WorkflowStep::SelectListPattern => Some(WorkflowStep::ReviewFileList),
WorkflowStep::ReviewFileList => Some(WorkflowStep::SelectRenamePattern),
WorkflowStep::SelectRenamePattern => Some(WorkflowStep::ReviewPreview),
WorkflowStep::ReviewPreview => Some(WorkflowStep::ConfigureApplyOptions),
WorkflowStep::ConfigureApplyOptions => Some(WorkflowStep::ConfirmAndExecute),
WorkflowStep::ConfirmAndExecute => Some(WorkflowStep::ShowResults),
WorkflowStep::ShowResults => Some(WorkflowStep::Complete),
WorkflowStep::Complete => None,
}
}
#[allow(dead_code)]
pub fn previous(self) -> Option<Self> {
match self {
WorkflowStep::Start => None,
WorkflowStep::SelectDirectory => Some(WorkflowStep::Start),
WorkflowStep::SelectListPattern => Some(WorkflowStep::SelectDirectory),
WorkflowStep::ReviewFileList => Some(WorkflowStep::SelectListPattern),
WorkflowStep::SelectRenamePattern => Some(WorkflowStep::ReviewFileList),
WorkflowStep::ReviewPreview => Some(WorkflowStep::SelectRenamePattern),
WorkflowStep::ConfigureApplyOptions => Some(WorkflowStep::ReviewPreview),
WorkflowStep::ConfirmAndExecute => Some(WorkflowStep::ConfigureApplyOptions),
WorkflowStep::ShowResults => Some(WorkflowStep::ConfirmAndExecute),
WorkflowStep::Complete => Some(WorkflowStep::ShowResults),
}
}
#[allow(dead_code)]
pub fn name(self) -> &'static str {
match self {
WorkflowStep::Start => "Start",
WorkflowStep::SelectDirectory => "Select Directory",
WorkflowStep::SelectListPattern => "Select Files",
WorkflowStep::ReviewFileList => "Review File List",
WorkflowStep::SelectRenamePattern => "Define Pattern",
WorkflowStep::ReviewPreview => "Review Preview",
WorkflowStep::ConfigureApplyOptions => "Configure Options",
WorkflowStep::ConfirmAndExecute => "Confirm & Execute",
WorkflowStep::ShowResults => "Results",
WorkflowStep::Complete => "Complete",
}
}
#[allow(dead_code)]
pub fn step_number(self) -> usize {
match self {
WorkflowStep::Start => 1,
WorkflowStep::SelectDirectory => 2,
WorkflowStep::SelectListPattern => 3,
WorkflowStep::ReviewFileList => 4,
WorkflowStep::SelectRenamePattern => 5,
WorkflowStep::ReviewPreview => 6,
WorkflowStep::ConfigureApplyOptions => 7,
WorkflowStep::ConfirmAndExecute => 8,
WorkflowStep::ShowResults => 9,
WorkflowStep::Complete => 10,
}
}
#[allow(dead_code)]
pub const TOTAL_STEPS: usize = 10;
}
pub struct WorkflowNavigator {
step_stack: Vec<WorkflowStep>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransitionValidation {
Valid,
Invalid(String), }
impl WorkflowNavigator {
pub fn new() -> Self {
Self {
step_stack: vec![WorkflowStep::Start],
}
}
pub fn current_step(&self) -> WorkflowStep {
*self.step_stack.last().unwrap_or(&WorkflowStep::Start)
}
pub fn can_transition_to(&self, target: WorkflowStep, state: &crate::state::InteractiveState) -> TransitionValidation {
let current = self.current_step();
match target {
WorkflowStep::ReviewFileList => {
if state.list_patterns.is_empty() && state.list_files_from.is_none() {
return TransitionValidation::Invalid(
"No file patterns specified. Please select files first.".to_string()
);
}
}
WorkflowStep::ReviewPreview => {
if state.rename_pattern.is_none() {
return TransitionValidation::Invalid(
"No rename pattern specified. Please define a pattern first.".to_string()
);
}
if state.selected_files.is_empty() {
return TransitionValidation::Invalid(
"No files selected. Please select files first.".to_string()
);
}
}
WorkflowStep::ConfirmAndExecute => {
if state.preview_result.is_none() {
return TransitionValidation::Invalid(
"No preview available. Please generate a preview first.".to_string()
);
}
if state.selected_files.is_empty() {
return TransitionValidation::Invalid(
"No files selected.".to_string()
);
}
}
_ => {
}
}
TransitionValidation::Valid
}
pub fn go_next(&mut self) -> Option<WorkflowStep> {
let current = self.current_step();
if let Some(next) = current.next() {
self.step_stack.push(next);
Some(next)
} else {
None
}
}
pub fn go_next_validated(&mut self, state: &crate::state::InteractiveState) -> Result<WorkflowStep, String> {
let current = self.current_step();
if let Some(next) = current.next() {
match self.can_transition_to(next, state) {
TransitionValidation::Valid => {
self.step_stack.push(next);
Ok(next)
}
TransitionValidation::Invalid(reason) => {
Err(reason)
}
}
} else {
Err("Already at the end of the workflow".to_string())
}
}
pub fn go_back(&mut self) -> Option<WorkflowStep> {
if self.step_stack.len() > 1 {
self.step_stack.pop();
Some(self.current_step())
} else {
None
}
}
pub fn jump_to(&mut self, step: WorkflowStep) {
if let Some(pos) = self.step_stack.iter().position(|&s| s == step) {
self.step_stack.truncate(pos + 1);
} else {
self.step_stack.push(step);
}
}
}
impl Default for WorkflowNavigator {
fn default() -> Self {
Self::new()
}
}