A simple TUI Library written in Rust
at main 144 lines 4.6 kB view raw
1//! Deployment pipeline — animated tree showing Build → Test → Deploy phases. 2 3use std::thread; 4use std::time::Duration; 5 6use sly::block::Block; 7use sly::color::Color; 8use sly::style::{Span, Style}; 9use sly::terminal; 10use sly::view::View; 11use sly::widgets::{ 12 spinner, 13 tree::{self, NodeState, TreeNode}, 14}; 15 16fn main() { 17 println!("SLY — Deployment Pipeline"); 18 println!(); 19 20 let width = terminal::get_size() 21 .map(|(c, _)| c as usize) 22 .unwrap_or(80) 23 .min(70); 24 25 // Node indices: 26 // 0 = pipeline root 27 // 1 = Build 2 = install 3 = compile 4 = bundle 28 // 5 = Test 6 = unit 7 = integration 29 // 8 = Deploy 9 = push 10 = rollout 30 let mut states = [NodeState::Pending; 11]; 31 let spin = spinner::Spinner::dots().label("Running pipeline…"); 32 33 let initial = Block::vstack(vec![spin.frame(0), build_tree(&states)], 1); 34 let mut view = View::create(&initial, width); 35 36 // Transitions: list of (node_idx, new_state) applied at the end of each pause. 37 let transitions: Vec<(Vec<(usize, NodeState)>, u64)> = vec![ 38 (vec![(0, NodeState::Running), (1, NodeState::Running)], 200), 39 (vec![(2, NodeState::Running)], 600), 40 (vec![(2, NodeState::Done), (3, NodeState::Running)], 700), 41 (vec![(3, NodeState::Done), (4, NodeState::Running)], 600), 42 ( 43 vec![ 44 (4, NodeState::Done), 45 (1, NodeState::Done), 46 (5, NodeState::Running), 47 ], 48 350, 49 ), 50 (vec![(6, NodeState::Running)], 600), 51 (vec![(6, NodeState::Done), (7, NodeState::Running)], 750), 52 ( 53 vec![ 54 (7, NodeState::Done), 55 (5, NodeState::Done), 56 (8, NodeState::Running), 57 ], 58 350, 59 ), 60 (vec![(9, NodeState::Running)], 600), 61 (vec![(9, NodeState::Done), (10, NodeState::Running)], 800), 62 ( 63 vec![ 64 (10, NodeState::Done), 65 (8, NodeState::Done), 66 (0, NodeState::Done), 67 ], 68 300, 69 ), 70 ]; 71 72 let mut tick: usize = 0; 73 for (changes, ms) in &transitions { 74 for _ in 0..4 { 75 thread::sleep(Duration::from_millis(ms / 4)); 76 view.update(&Block::vstack( 77 vec![spin.frame(tick), build_tree(&states)], 78 1, 79 )); 80 tick += 1; 81 } 82 for &(idx, state) in changes { 83 states[idx] = state; 84 } 85 view.update(&Block::vstack( 86 vec![spin.frame(tick), build_tree(&states)], 87 1, 88 )); 89 tick += 1; 90 } 91 92 let done = Block::text(vec![vec![Span::styled( 93 "✓ Deployment complete", 94 Style::new().bold().fg(Color::Green), 95 )]]); 96 view.update(&Block::vstack(vec![done, build_tree(&states)], 1)); 97 view.finish(); 98 println!(); 99} 100 101fn node_label(text: &str, state: NodeState) -> Vec<Span> { 102 let style = match state { 103 NodeState::Running => Style::new().bold().fg(Color::Cyan), 104 NodeState::Done => Style::new().fg(Color::Green), 105 NodeState::Failed => Style::new().bold().fg(Color::Red), 106 _ => Style::new().dim(), 107 }; 108 vec![Span::styled(text.to_string(), style)] 109} 110 111fn build_tree(s: &[NodeState; 11]) -> Block { 112 let nodes = vec![TreeNode::with_children( 113 node_label("Deploy pipeline", s[0]), 114 s[0], 115 vec![ 116 TreeNode::with_children( 117 node_label("Build", s[1]), 118 s[1], 119 vec![ 120 TreeNode::new(node_label("Install deps", s[2]), s[2]), 121 TreeNode::new(node_label("Compile", s[3]), s[3]), 122 TreeNode::new(node_label("Bundle assets", s[4]), s[4]), 123 ], 124 ), 125 TreeNode::with_children( 126 node_label("Test", s[5]), 127 s[5], 128 vec![ 129 TreeNode::new(node_label("Unit tests", s[6]), s[6]), 130 TreeNode::new(node_label("Integration tests", s[7]), s[7]), 131 ], 132 ), 133 TreeNode::with_children( 134 node_label("Deploy", s[8]), 135 s[8], 136 vec![ 137 TreeNode::new(node_label("Push image", s[9]), s[9]), 138 TreeNode::new(node_label("Rollout", s[10]), s[10]), 139 ], 140 ), 141 ], 142 )]; 143 tree::to_block(&nodes) 144}