use crate::edit_control::edit_history_time_travel::*;
use crate::edit_control::ModifiableEdit;
use crate::classification::ClassifiedEdit;
use crossterm::{
cursor, execute, terminal,
event::{self, Event, KeyCode},
terminal::{ClearType},
style::{Color, Print, ResetColor, SetForegroundColor},
};
use std::io::{self, Write};
use std::time::SystemTime;
pub struct TimeTravelCLI {
pub time_system: EditHistoryTimeTravelSystem,
pub interface_state: TimeTravelInterfaceState,
pub view_mode: TimeTravelViewMode,
pub selected_timeline: Option<String>,
pub selected_position: Option<usize>,
pub status_message: String,
pub show_help: bool,
pub navigation_history: Vec<NavigationState>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TimeTravelInterfaceState {
TimelineOverview,
TimelineDetails,
EventNavigation,
BranchingInterface,
MergingInterface,
SearchInterface,
CheckpointManagement,
Configuration,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TimeTravelViewMode {
List,
Graph,
Chronological,
FileCentric,
Split,
}
#[derive(Debug, Clone)]
pub struct NavigationState {
pub timeline_id: String,
pub event_position: usize,
pub timestamp: SystemTime,
}
impl TimeTravelCLI {
pub fn new() -> Result<Self, String> {
let time_system = EditHistoryTimeTravelSystem::new(None);
Ok(Self {
time_system,
interface_state: TimeTravelInterfaceState::TimelineOverview,
view_mode: TimeTravelViewMode::List,
selected_timeline: None,
selected_position: None,
status_message: "Time Travel initialized".to_string(),
show_help: false,
navigation_history: Vec::new(),
})
}
pub fn with_time_system(time_system: EditHistoryTimeTravelSystem) -> Self {
Self {
time_system,
interface_state: TimeTravelInterfaceState::TimelineOverview,
view_mode: TimeTravelViewMode::List,
selected_timeline: None,
selected_position: None,
status_message: "Time Travel loaded".to_string(),
show_help: false,
navigation_history: Vec::new(),
}
}
pub fn start_interactive_session(&mut self) -> Result<(), String> {
terminal::enable_raw_mode().map_err(|e| format!("Failed to enable raw mode: {}", e))?;
execute!(io::stdout(), terminal::Clear(ClearType::All))
.map_err(|e| format!("Failed to clear terminal: {}", e))?;
let mut running = true;
while running {
self.render_interface()?;
if let Ok(event) = event::read() {
match self.handle_event(event) {
Ok(should_continue) => {
if !should_continue {
running = false;
}
}
Err(e) => {
self.status_message = format!("Error: {}", e);
}
}
}
}
terminal::disable_raw_mode().map_err(|e| format!("Failed to disable raw mode: {}", e))?;
execute!(io::stdout(), terminal::Clear(ClearType::All))
.map_err(|e| format!("Failed to clear terminal: {}", e))?;
Ok(())
}
fn render_interface(&self) -> Result<(), String> {
execute!(io::stdout(), cursor::MoveTo(0, 0))
.map_err(|e| format!("Failed to move cursor: {}", e))?;
self.render_header()?;
match self.interface_state {
TimeTravelInterfaceState::TimelineOverview => self.render_timeline_overview()?,
TimeTravelInterfaceState::TimelineDetails => self.render_timeline_details()?,
TimeTravelInterfaceState::EventNavigation => self.render_event_navigation()?,
TimeTravelInterfaceState::BranchingInterface => self.render_branching_interface()?,
TimeTravelInterfaceState::MergingInterface => self.render_merging_interface()?,
TimeTravelInterfaceState::SearchInterface => self.render_search_interface()?,
TimeTravelInterfaceState::CheckpointManagement => self.render_checkpoint_management()?,
TimeTravelInterfaceState::Configuration => self.render_configuration()?,
}
self.render_controls()?;
self.render_status()?;
if self.show_help {
self.render_help_panel()?;
}
io::stdout().flush().map_err(|e| format!("Failed to flush stdout: {}", e))?;
Ok(())
}
fn render_header(&self) -> Result<(), String> {
execute!(io::stdout(),
SetForegroundColor(Color::Cyan),
Print("🕰️ SOMA-CORE Time Travel System\n"),
Print("═══════════════════════════════════════════════════════════\n"),
ResetColor,
Print(&format!("State: {:?} | View: {:?} | Timelines: {}\n",
self.interface_state, self.view_mode, self.time_system.timelines.len())),
Print("\n")
).map_err(|e| format!("Failed to render header: {}", e))?;
Ok(())
}
fn render_timeline_overview(&self) -> Result<(), String> {
execute!(io::stdout(),
SetForegroundColor(Color::Green),
Print("📋 Timeline Overview\n"),
Print("═══════════════════\n"),
ResetColor
).map_err(|e| format!("Failed to render overview header: {}", e))?;
if self.time_system.timelines.is_empty() {
execute!(io::stdout(),
Print("No timelines found. Press 'N' to create a new timeline.\n")
).map_err(|e| format!("Failed to render empty message: {}", e))?;
} else {
for (timeline_id, timeline) in &self.time_system.timelines {
let marker = if self.selected_timeline.as_ref() == Some(timeline_id) { ">" } else { " " };
let status_color = match timeline.state {
TimelineState::Active => Color::Green,
TimelineState::Inactive => Color::Yellow,
TimelineState::Archived => Color::Blue,
TimelineState::Merged => Color::Magenta,
TimelineState::Deleted => Color::Red,
};
execute!(io::stdout(),
Print(&format!("{} ", marker)),
SetForegroundColor(status_color),
Print(&format!("{:<20}", timeline.name)),
ResetColor,
Print(&format!(" | Events: {:3} | State: {:?}\n",
timeline.events.len(), timeline.state))
).map_err(|e| format!("Failed to render timeline: {}", e))?;
if let Some(parent) = &timeline.parent_timeline {
execute!(io::stdout(),
Print(&format!(" ↳ Branched from: {}\n",
self.time_system.timelines.get(parent)
.map(|t| t.name.as_str())
.unwrap_or("Unknown")))
).map_err(|e| format!("Failed to render branch info: {}", e))?;
}
if !timeline.child_timelines.is_empty() {
execute!(io::stdout(),
Print(&format!(" ↳ Children: {}\n",
timeline.child_timelines.len()))
).map_err(|e| format!("Failed to render children info: {}", e))?;
}
}
}
Ok(())
}
fn render_timeline_details(&self) -> Result<(), String> {
let timeline_id = self.selected_timeline.as_ref()
.ok_or("No timeline selected")?;
let timeline = self.time_system.timelines.get(timeline_id)
.ok_or("Selected timeline not found")?;
execute!(io::stdout(),
SetForegroundColor(Color::Green),
Print(&format!("📊 Timeline Details: {}\n", timeline.name)),
Print("═══════════════════════════════\n"),
ResetColor,
Print(&format!("Description: {}\n", timeline.description)),
Print(&format!("State: {:?}\n", timeline.state)),
Print(&format!("Events: {}\n", timeline.events.len())),
Print(&format!("Current Position: {}\n", timeline.current_position)),
Print(&format!("Created: {:?}\n", timeline.metadata.created_at)),
Print(&format!("Modified: {:?}\n", timeline.metadata.modified_at)),
Print("\n")
).map_err(|e| format!("Failed to render timeline details: {}", e))?;
execute!(io::stdout(),
SetForegroundColor(Color::Cyan),
Print("📝 Recent Events:\n"),
ResetColor
).map_err(|e| format!("Failed to render events header: {}", e))?;
let events_to_show = timeline.events.iter()
.enumerate()
.rev()
.take(10)
.collect::<Vec<_>>();
for (pos, event) in events_to_show.iter().rev() {
let marker = if *pos == timeline.current_position { ">" } else { " " };
let event_type_color = match event.event_type {
EditEventType::Creation => Color::Green,
EditEventType::Modification => Color::Yellow,
EditEventType::Checkpoint => Color::Blue,
EditEventType::Rollback => Color::Red,
_ => Color::White,
};
execute!(io::stdout(),
Print(&format!("{} {:3}: ", marker, pos)),
SetForegroundColor(event_type_color),
Print(&format!("{:12}", format!("{:?}", event.event_type))),
ResetColor,
Print(&format!(" | {}\n", event.metadata.description))
).map_err(|e| format!("Failed to render event: {}", e))?;
}
Ok(())
}
fn render_event_navigation(&self) -> Result<(), String> {
let timeline_id = self.selected_timeline.as_ref()
.ok_or("No timeline selected")?;
let timeline = self.time_system.timelines.get(timeline_id)
.ok_or("Selected timeline not found")?;
execute!(io::stdout(),
SetForegroundColor(Color::Green),
Print("🧭 Event Navigation\n"),
Print("═══════════════════\n"),
ResetColor,
Print(&format!("Timeline: {} | Position: {} / {}\n",
timeline.name, timeline.current_position + 1, timeline.events.len())),
Print("\n")
).map_err(|e| format!("Failed to render navigation header: {}", e))?;
if let Some(current_event) = timeline.events.get(timeline.current_position) {
execute!(io::stdout(),
SetForegroundColor(Color::Cyan),
Print("📍 Current Event:\n"),
ResetColor,
Print(&format!("ID: {}\n", current_event.id)),
Print(&format!("Type: {:?}\n", current_event.event_type)),
Print(&format!("Description: {}\n", current_event.metadata.description)),
Print(&format!("File: {}\n", current_event.edit.base_edit.file)),
Print(&format!("Confidence: {:.1}%\n", current_event.edit.get_effective_confidence() * 100.0)),
Print(&format!("Timestamp: {:?}\n", current_event.timestamp)),
Print("\n")
).map_err(|e| format!("Failed to render current event: {}", e))?;
execute!(io::stdout(),
SetForegroundColor(Color::Yellow),
Print("💻 Code Preview:\n"),
ResetColor
).map_err(|e| format!("Failed to render code header: {}", e))?;
let final_code = current_event.edit.compute_final_code();
let code_lines: Vec<&str> = final_code.lines().collect();
for (i, line) in code_lines.iter().take(10).enumerate() {
execute!(io::stdout(),
Print(&format!("{:3}: {}\n", i + 1, line))
).map_err(|e| format!("Failed to render code line: {}", e))?;
}
if code_lines.len() > 10 {
execute!(io::stdout(),
Print(&format!("... and {} more lines\n", code_lines.len() - 10))
).map_err(|e| format!("Failed to render code continuation: {}", e))?;
}
}
Ok(())
}
fn render_branching_interface(&self) -> Result<(), String> {
execute!(io::stdout(),
SetForegroundColor(Color::Green),
Print("🌿 Timeline Branching\n"),
Print("════════════════════\n"),
ResetColor,
Print("Create a new timeline branch from current position.\n"),
Print("\n"),
Print("Current Timeline: "),
SetForegroundColor(Color::Cyan),
Print(&format!("{}\n",
self.selected_timeline.as_ref()
.and_then(|id| self.time_system.timelines.get(id))
.map(|t| t.name.as_str())
.unwrap_or("None"))),
ResetColor,
Print("\n"),
Print("Enter branch name (press Enter to confirm, Escape to cancel):\n"),
Print("> ")
).map_err(|e| format!("Failed to render branching interface: {}", e))?;
Ok(())
}
fn render_merging_interface(&self) -> Result<(), String> {
execute!(io::stdout(),
SetForegroundColor(Color::Green),
Print("🔗 Timeline Merging\n"),
Print("═══════════════════\n"),
ResetColor,
Print("Merge timelines together.\n"),
Print("\n"),
Print("Available merge strategies:\n"),
Print("• fast-forward: Simple linear merge\n"),
Print("• three-way: Standard merge with conflict resolution\n"),
Print("• squash: Compress source timeline into single event\n"),
Print("\n")
).map_err(|e| format!("Failed to render merging interface: {}", e))?;
for (timeline_id, timeline) in &self.time_system.timelines {
if timeline.state == TimelineState::Active &&
Some(timeline_id) != self.selected_timeline.as_ref() {
execute!(io::stdout(),
Print(&format!(" {} ({})\n", timeline.name, timeline.events.len()))
).map_err(|e| format!("Failed to render merge candidate: {}", e))?;
}
}
Ok(())
}
fn render_search_interface(&self) -> Result<(), String> {
execute!(io::stdout(),
SetForegroundColor(Color::Green),
Print("🔍 History Search\n"),
Print("════════════════\n"),
ResetColor,
Print("Search through edit history.\n"),
Print("\n"),
Print("Search options:\n"),
Print("• Text search in descriptions\n"),
Print("• Filter by file patterns\n"),
Print("• Filter by time range\n"),
Print("• Filter by confidence level\n"),
Print("• Filter by event types\n"),
Print("\n"),
Print("Enter search query: ")
).map_err(|e| format!("Failed to render search interface: {}", e))?;
Ok(())
}
fn render_checkpoint_management(&self) -> Result<(), String> {
execute!(io::stdout(),
SetForegroundColor(Color::Green),
Print("📍 Checkpoint Management\n"),
Print("═══════════════════════\n"),
ResetColor,
Print("Manage timeline checkpoints.\n"),
Print("\n")
).map_err(|e| format!("Failed to render checkpoint header: {}", e))?;
let mut checkpoint_count = 0;
for timeline in self.time_system.timelines.values() {
for event in &timeline.events {
if event.event_type == EditEventType::Checkpoint {
checkpoint_count += 1;
execute!(io::stdout(),
Print(&format!("📍 {} | {} | {}\n",
event.metadata.description,
timeline.name,
event.timestamp.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default().as_secs()))
).map_err(|e| format!("Failed to render checkpoint: {}", e))?;
}
}
}
if checkpoint_count == 0 {
execute!(io::stdout(),
Print("No checkpoints found. Press 'C' to create a checkpoint.\n")
).map_err(|e| format!("Failed to render no checkpoints message: {}", e))?;
}
Ok(())
}
fn render_configuration(&self) -> Result<(), String> {
execute!(io::stdout(),
SetForegroundColor(Color::Green),
Print("⚙️ Configuration\n"),
Print("════════════════\n"),
ResetColor,
Print(&format!("Max Memory Events: {}\n", self.time_system.config.max_memory_events)),
Print(&format!("Auto Checkpoint Frequency: {}\n", self.time_system.config.auto_checkpoint_frequency)),
Print(&format!("Max Timeline Depth: {}\n", self.time_system.config.max_timeline_depth)),
Print(&format!("Git Integration: {}\n", self.time_system.config.git_integration_enabled)),
Print(&format!("Auto Merge: {}\n", self.time_system.config.auto_merge_timelines)),
Print("\n"),
Print("Performance Settings:\n"),
Print(&format!(" Lazy Loading: {}\n", self.time_system.config.performance_settings.lazy_loading)),
Print(&format!(" Event Caching: {}\n", self.time_system.config.performance_settings.event_caching)),
Print(&format!(" Background Indexing: {}\n", self.time_system.config.performance_settings.background_indexing)),
Print(&format!(" Max Memory: {} MB\n", self.time_system.config.performance_settings.max_memory_mb)),
).map_err(|e| format!("Failed to render configuration: {}", e))?;
Ok(())
}
fn render_controls(&self) -> Result<(), String> {
execute!(io::stdout(),
Print("\n"),
SetForegroundColor(Color::Yellow),
Print("🎮 Controls: "),
ResetColor
).map_err(|e| format!("Failed to render controls header: {}", e))?;
match self.interface_state {
TimeTravelInterfaceState::TimelineOverview => {
execute!(io::stdout(),
Print("↑↓ Select | Enter View | N New | D Delete | B Branch | M Merge | S Search | Q Quit")
).map_err(|e| format!("Failed to render overview controls: {}", e))?;
}
TimeTravelInterfaceState::EventNavigation => {
execute!(io::stdout(),
Print("←→ Navigate | J Jump | C Checkpoint | R Rollback | Esc Back | ? Help")
).map_err(|e| format!("Failed to render navigation controls: {}", e))?;
}
_ => {
execute!(io::stdout(),
Print("Esc Back | ? Help | Q Quit")
).map_err(|e| format!("Failed to render default controls: {}", e))?;
}
}
Ok(())
}
fn render_status(&self) -> Result<(), String> {
execute!(io::stdout(),
Print("\n"),
SetForegroundColor(Color::Blue),
Print("📊 Status: "),
ResetColor,
Print(&self.status_message),
Print("\n")
).map_err(|e| format!("Failed to render status: {}", e))?;
Ok(())
}
fn render_help_panel(&self) -> Result<(), String> {
execute!(io::stdout(),
Print("\n"),
SetForegroundColor(Color::Magenta),
Print("❓ Help Panel\n"),
Print("════════════\n"),
ResetColor,
Print("Time Travel System Commands:\n"),
Print("• Timeline Management: Create, delete, branch, merge timelines\n"),
Print("• Event Navigation: Move through edit history timeline\n"),
Print("• Search & Filter: Find specific edits or patterns\n"),
Print("• Checkpoints: Create and restore stable points\n"),
Print("• Git Integration: Sync with Git commits and branches\n"),
Print("\n"),
Print("Navigation:\n"),
Print("• Arrow keys: Navigate through timelines and events\n"),
Print("• Enter: Select or confirm action\n"),
Print("• Escape: Go back or cancel operation\n"),
Print("• Q: Quit time travel system\n"),
Print("• ?: Toggle this help panel\n"),
).map_err(|e| format!("Failed to render help panel: {}", e))?;
Ok(())
}
fn handle_event(&mut self, event: Event) -> Result<bool, String> {
match event {
Event::Key(key_event) => {
match key_event.code {
KeyCode::Char('q') | KeyCode::Char('Q') => {
return Ok(false); }
KeyCode::Char('?') => {
self.show_help = !self.show_help;
}
KeyCode::Esc => {
self.handle_escape()?;
}
_ => {
self.handle_state_specific_key(key_event.code)?;
}
}
}
_ => {}
}
Ok(true)
}
fn handle_escape(&mut self) -> Result<(), String> {
match self.interface_state {
TimeTravelInterfaceState::TimelineOverview => {
}
_ => {
self.interface_state = TimeTravelInterfaceState::TimelineOverview;
self.status_message = "Returned to timeline overview".to_string();
}
}
Ok(())
}
fn handle_state_specific_key(&mut self, key: KeyCode) -> Result<(), String> {
match self.interface_state {
TimeTravelInterfaceState::TimelineOverview => {
self.handle_overview_keys(key)?;
}
TimeTravelInterfaceState::TimelineDetails => {
self.handle_details_keys(key)?;
}
TimeTravelInterfaceState::EventNavigation => {
self.handle_navigation_keys(key)?;
}
_ => {
}
}
Ok(())
}
fn handle_overview_keys(&mut self, key: KeyCode) -> Result<(), String> {
match key {
KeyCode::Char('n') | KeyCode::Char('N') => {
self.create_new_timeline()?;
}
KeyCode::Enter => {
if self.selected_timeline.is_some() {
self.interface_state = TimeTravelInterfaceState::TimelineDetails;
self.status_message = "Viewing timeline details".to_string();
}
}
KeyCode::Char('b') | KeyCode::Char('B') => {
if self.selected_timeline.is_some() {
self.interface_state = TimeTravelInterfaceState::BranchingInterface;
self.status_message = "Timeline branching mode".to_string();
}
}
KeyCode::Char('s') | KeyCode::Char('S') => {
self.interface_state = TimeTravelInterfaceState::SearchInterface;
self.status_message = "History search mode".to_string();
}
KeyCode::Up => {
self.select_previous_timeline();
}
KeyCode::Down => {
self.select_next_timeline();
}
_ => {}
}
Ok(())
}
fn handle_details_keys(&mut self, key: KeyCode) -> Result<(), String> {
match key {
KeyCode::Char('e') | KeyCode::Char('E') => {
self.interface_state = TimeTravelInterfaceState::EventNavigation;
self.status_message = "Event navigation mode".to_string();
}
KeyCode::Char('c') | KeyCode::Char('C') => {
self.interface_state = TimeTravelInterfaceState::CheckpointManagement;
self.status_message = "Checkpoint management mode".to_string();
}
_ => {}
}
Ok(())
}
fn handle_navigation_keys(&mut self, key: KeyCode) -> Result<(), String> {
match key {
KeyCode::Left => {
self.navigate_to_previous_event()?;
}
KeyCode::Right => {
self.navigate_to_next_event()?;
}
KeyCode::Char('c') | KeyCode::Char('C') => {
self.create_checkpoint_at_current_position()?;
}
KeyCode::Char('j') | KeyCode::Char('J') => {
self.jump_to_event()?;
}
_ => {}
}
Ok(())
}
fn create_new_timeline(&mut self) -> Result<(), String> {
let timeline_id = self.time_system.create_timeline("new_timeline", "New timeline")?;
self.selected_timeline = Some(timeline_id);
self.status_message = "Created new timeline".to_string();
Ok(())
}
fn select_previous_timeline(&mut self) {
let timeline_ids: Vec<_> = self.time_system.timelines.keys().collect();
if !timeline_ids.is_empty() {
if let Some(current) = &self.selected_timeline {
if let Some(pos) = timeline_ids.iter().position(|id| *id == current) {
let new_pos = if pos > 0 { pos - 1 } else { timeline_ids.len() - 1 };
self.selected_timeline = Some(timeline_ids[new_pos].clone());
}
} else {
self.selected_timeline = Some(timeline_ids[0].clone());
}
}
}
fn select_next_timeline(&mut self) {
let timeline_ids: Vec<_> = self.time_system.timelines.keys().collect();
if !timeline_ids.is_empty() {
if let Some(current) = &self.selected_timeline {
if let Some(pos) = timeline_ids.iter().position(|id| *id == current) {
let new_pos = (pos + 1) % timeline_ids.len();
self.selected_timeline = Some(timeline_ids[new_pos].clone());
}
} else {
self.selected_timeline = Some(timeline_ids[0].clone());
}
}
}
fn navigate_to_previous_event(&mut self) -> Result<(), String> {
if let Some(timeline_id) = &self.selected_timeline {
if let Some(timeline) = self.time_system.timelines.get_mut(timeline_id) {
if timeline.current_position > 0 {
timeline.current_position -= 1;
self.status_message = format!("Moved to event {}", timeline.current_position);
} else {
self.status_message = "Already at first event".to_string();
}
}
}
Ok(())
}
fn navigate_to_next_event(&mut self) -> Result<(), String> {
if let Some(timeline_id) = &self.selected_timeline {
if let Some(timeline) = self.time_system.timelines.get_mut(timeline_id) {
if timeline.current_position < timeline.events.len().saturating_sub(1) {
timeline.current_position += 1;
self.status_message = format!("Moved to event {}", timeline.current_position);
} else {
self.status_message = "Already at last event".to_string();
}
}
}
Ok(())
}
fn create_checkpoint_at_current_position(&mut self) -> Result<(), String> {
let checkpoint_id = self.time_system.create_checkpoint_at_current_position("manual_checkpoint")?;
self.status_message = format!("Created checkpoint: {}", checkpoint_id);
Ok(())
}
fn jump_to_event(&mut self) -> Result<(), String> {
self.status_message = "Jump to event (not implemented in demo)".to_string();
Ok(())
}
pub fn add_edits_to_timeline(&mut self, edits: Vec<ModifiableEdit>) -> Result<(), String> {
if self.time_system.timelines.is_empty() {
self.time_system.create_timeline("main", "Main timeline")?;
}
for edit in edits {
self.time_system.add_edit_event(edit, "Added via CLI")?;
}
self.status_message = format!("Added {} edits to timeline",
self.time_system.timelines.values().next()
.map(|t| t.events.len())
.unwrap_or(0));
Ok(())
}
}
pub fn interactive_time_travel_decision(
edits: Vec<ModifiableEdit>,
_classifications: Vec<ClassifiedEdit>
) -> Result<bool, String> {
println!("🕰️ Time Travel System Available");
println!("═══════════════════════════════════");
println!("Would you like to use the advanced time travel system?");
println!();
println!("Time travel provides:");
println!("• Complete edit history with timeline branching");
println!("• Navigate through different edit paths");
println!("• Create and restore checkpoints");
println!("• Search through historical changes");
println!("• Merge and compare different edit timelines");
println!();
print!("Use Time Travel system? [y/N]: ");
io::stdout().flush().map_err(|e| format!("Failed to flush stdout: {}", e))?;
let mut input = String::new();
io::stdin().read_line(&mut input).map_err(|e| format!("Failed to read input: {}", e))?;
if input.trim().to_lowercase().starts_with('y') {
let mut time_travel_cli = TimeTravelCLI::new()?;
time_travel_cli.add_edits_to_timeline(edits)?;
println!("Launching Time Travel interface...");
time_travel_cli.start_interactive_session()?;
println!("Time travel session completed");
Ok(true)
} else {
println!("Proceeding without time travel");
Ok(false)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::agents::gpt4_agent::ProposedEdit;
fn create_test_edit() -> ModifiableEdit {
let proposed = ProposedEdit {
file: "test.rs".to_string(),
line_range: (1, 5),
new_code: "fn test() { println!(\"test\"); }".to_string(),
reason: "Test function".to_string(),
confidence: 0.8,
};
ModifiableEdit::from_proposed_edit(proposed)
}
#[test]
fn test_time_travel_cli_creation() {
let cli = TimeTravelCLI::new().unwrap();
assert_eq!(cli.interface_state, TimeTravelInterfaceState::TimelineOverview);
assert_eq!(cli.view_mode, TimeTravelViewMode::List);
assert!(cli.selected_timeline.is_none());
}
#[test]
fn test_add_edits_to_timeline() {
let mut cli = TimeTravelCLI::new().unwrap();
let edits = vec![create_test_edit()];
cli.add_edits_to_timeline(edits).unwrap();
assert!(!cli.time_system.timelines.is_empty());
assert!(cli.time_system.current_timeline.is_some());
}
#[test]
fn test_timeline_selection() {
let mut cli = TimeTravelCLI::new().unwrap();
cli.time_system.create_timeline("test1", "Test 1").unwrap();
cli.time_system.create_timeline("test2", "Test 2").unwrap();
cli.select_next_timeline();
assert!(cli.selected_timeline.is_some());
cli.select_previous_timeline();
assert!(cli.selected_timeline.is_some());
}
}