use crate::edit_control::{
ConditionalLogicSystem, Condition, ConditionType, ClassificationCriteria,
TestConfiguration, ConditionalChain, ChainLink, ChainFailureStrategy,
ConditionalApplicationResult, ModifiableEdit
};
use crate::classification::{EditCategory, EditRecommendation, EditPriority};
use std::io::{self, Write};
use crossterm::{
terminal::{enable_raw_mode, disable_raw_mode, Clear, ClearType},
event::{self, Event, KeyCode, KeyEvent},
cursor,
style::{Color, SetForegroundColor, ResetColor, SetBackgroundColor, Attribute, SetAttribute},
};
pub struct ConditionalLogicCLI {
system: ConditionalLogicSystem,
current_view: ConditionalView,
selected_index: usize,
scroll_offset: usize,
message: Option<String>,
input_buffer: String,
input_mode: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConditionalView {
MainMenu,
ConditionList,
ChainList,
SessionList,
CreateCondition,
CreateChain,
ApplyConditional,
ExecuteChain,
ViewHistory,
ConditionDetails(String),
ChainDetails(String),
}
impl ConditionalLogicCLI {
pub fn new() -> Self {
Self {
system: ConditionalLogicSystem::new(),
current_view: ConditionalView::MainMenu,
selected_index: 0,
scroll_offset: 0,
message: None,
input_buffer: String::new(),
input_mode: false,
}
}
pub fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
enable_raw_mode()?;
let result = self.run_loop();
disable_raw_mode()?;
result
}
fn run_loop(&mut self) -> Result<(), Box<dyn std::error::Error>> {
loop {
self.draw()?;
if let Event::Key(key_event) = event::read()? {
if self.handle_key_event(key_event)? {
break; }
}
}
Ok(())
}
fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<bool, Box<dyn std::error::Error>> {
if self.input_mode {
return self.handle_input_mode(key_event);
}
match key_event.code {
KeyCode::Char('q') | KeyCode::Esc => return Ok(true), KeyCode::Up => self.move_selection(-1),
KeyCode::Down => self.move_selection(1),
KeyCode::Enter => self.handle_selection()?,
KeyCode::Char('h') => self.show_help(),
KeyCode::Char('c') => self.current_view = ConditionalView::CreateCondition,
KeyCode::Char('n') => self.current_view = ConditionalView::CreateChain,
KeyCode::Char('s') => self.current_view = ConditionalView::SessionList,
KeyCode::Char('a') => self.current_view = ConditionalView::ApplyConditional,
KeyCode::Char('e') => self.current_view = ConditionalView::ExecuteChain,
KeyCode::Char('r') => self.current_view = ConditionalView::ViewHistory,
KeyCode::Backspace => self.go_back(),
_ => {}
}
Ok(false)
}
fn handle_input_mode(&mut self, key_event: KeyEvent) -> Result<bool, Box<dyn std::error::Error>> {
match key_event.code {
KeyCode::Enter => {
self.input_mode = false;
self.process_input()?;
}
KeyCode::Esc => {
self.input_mode = false;
self.input_buffer.clear();
}
KeyCode::Backspace => {
self.input_buffer.pop();
}
KeyCode::Char(c) => {
self.input_buffer.push(c);
}
_ => {}
}
Ok(false)
}
fn process_input(&mut self) -> Result<(), Box<dyn std::error::Error>> {
match &self.current_view {
ConditionalView::CreateCondition => {
self.create_condition_from_input()?;
}
ConditionalView::CreateChain => {
self.create_chain_from_input()?;
}
_ => {}
}
self.input_buffer.clear();
Ok(())
}
fn create_condition_from_input(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let parts: Vec<&str> = self.input_buffer.split(':').collect();
if parts.len() < 2 {
self.message = Some("Invalid format. Use: type:name:config".to_string());
return Ok(());
}
let condition_type = parts[0];
let name = parts[1].to_string();
let id = format!("{}_condition", name.replace(' ', "_").to_lowercase());
let condition = match condition_type {
"test" => {
let test_config = if parts.len() > 2 {
TestConfiguration::new(parts[2].to_string())
} else {
TestConfiguration::cargo_test()
};
Condition::new_test_condition(id.clone(), name, test_config)
}
"classification" => {
let criteria = if parts.len() > 2 {
match parts[2] {
"safe" => ClassificationCriteria::RequiredCategory(EditCategory::Safe),
"critical" => ClassificationCriteria::RequiredCategory(EditCategory::Critical),
"experimental" => ClassificationCriteria::RequiredCategory(EditCategory::Experimental),
_ => ClassificationCriteria::MinConfidence(0.8),
}
} else {
ClassificationCriteria::RequiredCategory(EditCategory::Safe)
};
Condition::new_classification_condition(id.clone(), name, criteria)
}
_ => {
self.message = Some("Unknown condition type. Use: test, classification".to_string());
return Ok(());
}
};
match self.system.add_condition(condition) {
Ok(_) => {
self.message = Some(format!("Condition '{}' created successfully", id));
self.current_view = ConditionalView::ConditionList;
}
Err(e) => {
self.message = Some(format!("Failed to create condition: {}", e));
}
}
Ok(())
}
fn create_chain_from_input(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let parts: Vec<&str> = self.input_buffer.split(':').collect();
if parts.len() < 2 {
self.message = Some("Invalid format. Use: name:description".to_string());
return Ok(());
}
let name = parts[0].to_string();
let description = parts.get(1).unwrap_or(&"").to_string();
let id = format!("{}_chain", name.replace(' ', "_").to_lowercase());
let mut chain = ConditionalChain::new(id.clone(), name);
chain.description = description;
match self.system.create_chain(chain) {
Ok(_) => {
self.message = Some(format!("Chain '{}' created successfully", id));
self.current_view = ConditionalView::ChainList;
}
Err(e) => {
self.message = Some(format!("Failed to create chain: {}", e));
}
}
Ok(())
}
fn handle_selection(&mut self) -> Result<(), Box<dyn std::error::Error>> {
match &self.current_view {
ConditionalView::MainMenu => {
match self.selected_index {
0 => self.current_view = ConditionalView::ConditionList,
1 => self.current_view = ConditionalView::ChainList,
2 => self.current_view = ConditionalView::SessionList,
3 => self.current_view = ConditionalView::CreateCondition,
4 => self.current_view = ConditionalView::CreateChain,
5 => self.current_view = ConditionalView::ApplyConditional,
6 => self.current_view = ConditionalView::ExecuteChain,
7 => self.current_view = ConditionalView::ViewHistory,
_ => {}
}
}
ConditionalView::CreateCondition | ConditionalView::CreateChain => {
self.input_mode = true;
self.input_buffer.clear();
}
_ => {}
}
Ok(())
}
fn move_selection(&mut self, delta: i32) {
let max_items = self.get_max_items();
if max_items == 0 {
return;
}
if delta > 0 {
self.selected_index = (self.selected_index + 1).min(max_items - 1);
} else if delta < 0 && self.selected_index > 0 {
self.selected_index = self.selected_index.saturating_sub(1);
}
let visible_lines = 20; if self.selected_index >= self.scroll_offset + visible_lines {
self.scroll_offset = self.selected_index - visible_lines + 1;
} else if self.selected_index < self.scroll_offset {
self.scroll_offset = self.selected_index;
}
}
fn get_max_items(&self) -> usize {
match &self.current_view {
ConditionalView::MainMenu => 8,
ConditionalView::ConditionList => self.system.get_execution_history().len(),
ConditionalView::ChainList => self.system.get_execution_history().len(),
ConditionalView::SessionList => self.system.get_active_sessions().len(),
ConditionalView::ViewHistory => self.system.get_execution_history().len(),
_ => 0,
}
}
fn go_back(&mut self) {
self.current_view = ConditionalView::MainMenu;
self.selected_index = 0;
self.scroll_offset = 0;
self.message = None;
}
fn show_help(&mut self) {
self.message = Some(
"Keys: ↑/↓=Navigate, Enter=Select, q/Esc=Exit, h=Help, c=Create Condition, n=New Chain, s=Sessions, a=Apply, e=Execute, r=History, Backspace=Back".to_string()
);
}
fn draw(&mut self) -> Result<(), Box<dyn std::error::Error>> {
print!("{}{}", Clear(ClearType::All), cursor::MoveTo(0, 0));
print!("{}{}", SetForegroundColor(Color::Cyan), SetAttribute(Attribute::Bold));
println!("🔗 SOMA ConditionalLogicSystem Interactive CLI");
print!("{}{}", ResetColor, SetAttribute(Attribute::Reset));
println!("═══════════════════════════════════════════════");
match &self.current_view {
ConditionalView::MainMenu => self.draw_main_menu(),
ConditionalView::ConditionList => self.draw_condition_list(),
ConditionalView::ChainList => self.draw_chain_list(),
ConditionalView::SessionList => self.draw_session_list(),
ConditionalView::CreateCondition => self.draw_create_condition(),
ConditionalView::CreateChain => self.draw_create_chain(),
ConditionalView::ApplyConditional => self.draw_apply_conditional(),
ConditionalView::ExecuteChain => self.draw_execute_chain(),
ConditionalView::ViewHistory => self.draw_history(),
ConditionalView::ConditionDetails(id) => self.draw_condition_details(id),
ConditionalView::ChainDetails(id) => self.draw_chain_details(id),
}
if let Some(ref message) = self.message {
println!();
print!("{}", SetForegroundColor(Color::Yellow));
println!("💬 {}", message);
print!("{}", ResetColor);
}
println!();
print!("{}", SetForegroundColor(Color::DarkGrey));
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!("Press 'h' for help, 'q' to quit");
print!("{}", ResetColor);
io::stdout().flush()?;
Ok(())
}
fn draw_main_menu(&self) {
println!("📋 Main Menu");
println!();
let menu_items = [
"📝 View Conditions",
"🔗 View Chains",
"📊 View Sessions",
"➕ Create Condition",
"🆕 Create Chain",
"✅ Apply Conditional Edit",
"▶️ Execute Chain",
"📚 View Execution History",
];
for (i, item) in menu_items.iter().enumerate() {
if i == self.selected_index {
print!("{}{}", SetBackgroundColor(Color::Blue), SetForegroundColor(Color::White));
println!("► {}", item);
print!("{}{}", ResetColor, SetBackgroundColor(Color::Reset));
} else {
println!(" {}", item);
}
}
}
fn draw_condition_list(&self) {
println!("📝 Conditions");
println!();
if self.system.get_execution_history().is_empty() {
println!("No conditions defined yet.");
println!("Press 'c' to create a new condition.");
} else {
println!("Conditions will be listed here when implementation is complete.");
}
}
fn draw_chain_list(&self) {
println!("🔗 Conditional Chains");
println!();
if self.system.get_execution_history().is_empty() {
println!("No chains defined yet.");
println!("Press 'n' to create a new chain.");
} else {
println!("Chains will be listed here when implementation is complete.");
}
}
fn draw_session_list(&self) {
println!("📊 Active Sessions");
println!();
let sessions = self.system.get_active_sessions();
if sessions.is_empty() {
println!("No active sessions.");
} else {
for (session_id, session) in sessions {
println!("🔖 {} ({})", session.name, session_id);
println!(" Created: {}", session.created_at.format("%Y-%m-%d %H:%M:%S"));
println!(" Edits Applied: {}", session.edits_applied.len());
println!();
}
}
}
fn draw_create_condition(&self) {
println!("➕ Create New Condition");
println!();
println!("Format: type:name:config");
println!("Types: test, classification");
println!("Examples:");
println!(" test:unit_tests:cargo test");
println!(" classification:safe_only:safe");
println!();
if self.input_mode {
print!("Enter condition: {}_", self.input_buffer);
} else {
println!("Press Enter to start input, Esc to cancel");
}
}
fn draw_create_chain(&self) {
println!("🆕 Create New Chain");
println!();
println!("Format: name:description");
println!("Example: deploy_chain:Deploy after tests pass");
println!();
if self.input_mode {
print!("Enter chain: {}_", self.input_buffer);
} else {
println!("Press Enter to start input, Esc to cancel");
}
}
fn draw_apply_conditional(&self) {
println!("✅ Apply Conditional Edit");
println!();
println!("This feature allows applying edits with conditions.");
println!("Implementation pending...");
}
fn draw_execute_chain(&self) {
println!("▶️ Execute Conditional Chain");
println!();
println!("This feature allows executing conditional chains.");
println!("Implementation pending...");
}
fn draw_history(&self) {
println!("📚 Execution History");
println!();
let history = self.system.get_execution_history();
if history.is_empty() {
println!("No execution history yet.");
} else {
for (i, record) in history.iter().enumerate() {
let status = if record.applied { "✅ Applied" } else { "❌ Failed" };
println!("{} [{}] Edit: {}", status, record.timestamp.format("%H:%M:%S"), record.edit_id);
println!(" Conditions: {:?}", record.conditions_evaluated);
println!(" Duration: {:?}", record.execution_time);
if i < history.len() - 1 {
println!();
}
}
}
}
fn draw_condition_details(&self, _condition_id: &str) {
println!("📝 Condition Details");
println!();
println!("Detailed condition view will be implemented here.");
}
fn draw_chain_details(&self, _chain_id: &str) {
println!("🔗 Chain Details");
println!();
println!("Detailed chain view will be implemented here.");
}
}
pub fn demonstrate_conditional_logic() -> Result<(), Box<dyn std::error::Error>> {
println!("🔗 ConditionalLogicSystem Demonstration");
println!("=======================================");
let mut system = ConditionalLogicSystem::new();
println!("\n1. Creating conditional session...");
let session_id = system.create_session("demo_session".to_string());
println!(" ✅ Session created: {}", session_id);
println!("\n2. Creating test condition...");
let test_config = TestConfiguration::cargo_test()
.with_timeout(300)
.with_required_pattern("test result: ok".to_string());
let test_condition = Condition::new_test_condition(
"unit_tests".to_string(),
"Unit Tests Must Pass".to_string(),
test_config
);
let condition_id = system.add_condition(test_condition)?;
println!(" ✅ Test condition created: {}", condition_id);
println!("\n3. Creating classification condition...");
let classification_condition = Condition::new_classification_condition(
"safe_edits_only".to_string(),
"Safe Edits Only".to_string(),
ClassificationCriteria::RequiredCategory(EditCategory::Safe)
);
let class_condition_id = system.add_condition(classification_condition)?;
println!(" ✅ Classification condition created: {}", class_condition_id);
println!("\n4. Creating conditional chain...");
let mut chain = ConditionalChain::new(
"safe_deploy_chain".to_string(),
"Safe Deployment Chain".to_string()
);
chain.description = "Deploy only after tests pass and edit is classified as safe".to_string();
chain.add_link(ChainLink {
id: "test_link".to_string(),
condition_id: condition_id.clone(),
edits: Vec::new(), failure_strategy: ChainFailureStrategy::StopChain,
});
chain.add_link(ChainLink {
id: "classification_link".to_string(),
condition_id: class_condition_id.clone(),
edits: Vec::new(), failure_strategy: ChainFailureStrategy::StopChain,
});
let chain_id = system.create_chain(chain)?;
println!(" ✅ Chain created: {}", chain_id);
println!("\n5. System Status:");
println!(" 📊 Active sessions: {}", system.get_active_sessions().len());
println!(" 📝 Conditions defined: 2");
println!(" 🔗 Chains defined: 1");
println!(" 📚 Execution history: {}", system.get_execution_history().len());
println!("\n6. Condition Success Rates:");
println!(" {} success rate: {:.1}%", condition_id, system.get_condition_success_rate(&condition_id) * 100.0);
println!(" {} success rate: {:.1}%", class_condition_id, system.get_condition_success_rate(&class_condition_id) * 100.0);
println!("\n✅ ConditionalLogicSystem demonstration completed!");
println!("\nTo try the interactive CLI, run: soma conditional-cli");
Ok(())
}
pub fn run_conditional_cli() -> Result<(), Box<dyn std::error::Error>> {
let mut cli = ConditionalLogicCLI::new();
cli.run()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_conditional_cli_creation() {
let cli = ConditionalLogicCLI::new();
assert_eq!(cli.current_view, ConditionalView::MainMenu);
assert_eq!(cli.selected_index, 0);
assert!(!cli.input_mode);
}
#[test]
fn test_view_navigation() {
let mut cli = ConditionalLogicCLI::new();
cli.move_selection(1);
assert_eq!(cli.selected_index, 1);
cli.move_selection(-1);
assert_eq!(cli.selected_index, 0);
}
#[test]
fn test_demonstration() {
assert!(demonstrate_conditional_logic().is_ok());
}
#[test]
fn test_input_parsing() {
let mut cli = ConditionalLogicCLI::new();
cli.current_view = ConditionalView::CreateCondition;
cli.input_buffer = "test:unit_tests:cargo test".to_string();
assert!(cli.process_input().is_ok());
}
#[test]
fn test_max_items_calculation() {
let cli = ConditionalLogicCLI::new();
assert_eq!(cli.get_max_items(), 8); }
}