//! Deployment pipeline — animated tree showing Build → Test → Deploy phases. use std::thread; use std::time::Duration; use sly::block::Block; use sly::color::Color; use sly::style::{Span, Style}; use sly::terminal; use sly::view::View; use sly::widgets::{ spinner, tree::{self, NodeState, TreeNode}, }; fn main() { println!("SLY — Deployment Pipeline"); println!(); let width = terminal::get_size() .map(|(c, _)| c as usize) .unwrap_or(80) .min(70); // Node indices: // 0 = pipeline root // 1 = Build 2 = install 3 = compile 4 = bundle // 5 = Test 6 = unit 7 = integration // 8 = Deploy 9 = push 10 = rollout let mut states = [NodeState::Pending; 11]; let spin = spinner::Spinner::dots().label("Running pipeline…"); let initial = Block::vstack(vec![spin.frame(0), build_tree(&states)], 1); let mut view = View::create(&initial, width); // Transitions: list of (node_idx, new_state) applied at the end of each pause. let transitions: Vec<(Vec<(usize, NodeState)>, u64)> = vec![ (vec![(0, NodeState::Running), (1, NodeState::Running)], 200), (vec![(2, NodeState::Running)], 600), (vec![(2, NodeState::Done), (3, NodeState::Running)], 700), (vec![(3, NodeState::Done), (4, NodeState::Running)], 600), ( vec![ (4, NodeState::Done), (1, NodeState::Done), (5, NodeState::Running), ], 350, ), (vec![(6, NodeState::Running)], 600), (vec![(6, NodeState::Done), (7, NodeState::Running)], 750), ( vec![ (7, NodeState::Done), (5, NodeState::Done), (8, NodeState::Running), ], 350, ), (vec![(9, NodeState::Running)], 600), (vec![(9, NodeState::Done), (10, NodeState::Running)], 800), ( vec![ (10, NodeState::Done), (8, NodeState::Done), (0, NodeState::Done), ], 300, ), ]; let mut tick: usize = 0; for (changes, ms) in &transitions { for _ in 0..4 { thread::sleep(Duration::from_millis(ms / 4)); view.update(&Block::vstack( vec![spin.frame(tick), build_tree(&states)], 1, )); tick += 1; } for &(idx, state) in changes { states[idx] = state; } view.update(&Block::vstack( vec![spin.frame(tick), build_tree(&states)], 1, )); tick += 1; } let done = Block::text(vec![vec![Span::styled( "✓ Deployment complete", Style::new().bold().fg(Color::Green), )]]); view.update(&Block::vstack(vec![done, build_tree(&states)], 1)); view.finish(); println!(); } fn node_label(text: &str, state: NodeState) -> Vec { let style = match state { NodeState::Running => Style::new().bold().fg(Color::Cyan), NodeState::Done => Style::new().fg(Color::Green), NodeState::Failed => Style::new().bold().fg(Color::Red), _ => Style::new().dim(), }; vec![Span::styled(text.to_string(), style)] } fn build_tree(s: &[NodeState; 11]) -> Block { let nodes = vec![TreeNode::with_children( node_label("Deploy pipeline", s[0]), s[0], vec![ TreeNode::with_children( node_label("Build", s[1]), s[1], vec![ TreeNode::new(node_label("Install deps", s[2]), s[2]), TreeNode::new(node_label("Compile", s[3]), s[3]), TreeNode::new(node_label("Bundle assets", s[4]), s[4]), ], ), TreeNode::with_children( node_label("Test", s[5]), s[5], vec![ TreeNode::new(node_label("Unit tests", s[6]), s[6]), TreeNode::new(node_label("Integration tests", s[7]), s[7]), ], ), TreeNode::with_children( node_label("Deploy", s[8]), s[8], vec![ TreeNode::new(node_label("Push image", s[9]), s[9]), TreeNode::new(node_label("Rollout", s[10]), s[10]), ], ), ], )]; tree::to_block(&nodes) }