Skip to main content

download/
download.rs

1//! A demo that reads like a real CLI tool installing something, with the dragon
2//! reveal as the download step. Paced like a true transfer and made for recording.
3//!
4//!   cargo run --example download                          # glow, built-in dragon
5//!   cargo run --example download -- rainbow               # rainbow palette
6//!   cargo run --example download -- geodesic serpent.txt  # geodesic spine trace
7//!   cargo run --example download -- rainbow art.txt       # rainbow, your own art
8
9use std::io::{self, Write};
10use std::thread;
11use std::time::{Duration, Instant};
12
13use crossterm::{
14    cursor::MoveTo,
15    execute,
16    style::{Color, Print, ResetColor, SetForegroundColor},
17    terminal::{Clear, ClearType},
18};
19use inkling::{ordering::Geodesic, render::Style, Loader};
20
21fn step(mark: &str, color: Color, msg: &str) {
22    let _ = execute!(
23        io::stdout(),
24        Print("  "),
25        SetForegroundColor(color),
26        Print(mark),
27        ResetColor,
28        Print(format!(" {msg}\r\n"))
29    );
30}
31
32fn pause(ms: u64) {
33    thread::sleep(Duration::from_millis(ms));
34}
35
36fn main() {
37    let args: Vec<String> = std::env::args().skip(1).collect();
38    let rainbow = args.iter().any(|a| a == "rainbow");
39    let geodesic = args.iter().any(|a| a == "geodesic");
40    let art_path = args.iter().find(|a| a.ends_with(".txt")).cloned();
41    let started = Instant::now();
42    let gold = Color::Rgb {
43        r: 232,
44        g: 180,
45        b: 85,
46    };
47
48    // Clear to a fresh screen and print the tool banner.
49    let _ = execute!(
50        io::stdout(),
51        Clear(ClearType::All),
52        MoveTo(0, 0),
53        Print("\r\n  "),
54        SetForegroundColor(gold),
55        Print("dragonctl"),
56        ResetColor,
57        Print("  v0.2.0\r\n\r\n")
58    );
59
60    step("\u{2713}", Color::Green, "Initializing demo environment");
61    pause(450);
62    step("\u{2713}", Color::Green, "Updating base image index");
63    pause(500);
64    step("\u{2193}", Color::Cyan, "Downloading base image");
65    let _ = execute!(io::stdout(), Print("\r\n"));
66
67    // The download itself, paced unevenly: a connect stall, a hiccup, a burst, a
68    // mid stall, and a slow tail.
69    let total: u64 = 48 * 1024 * 1024;
70    let mb = 1024.0 * 1024.0;
71    let schedule: &[(f64, u64)] = &[
72        (0.0, 600),
73        (5.0, 300),
74        (8.0, 250),
75        (8.0, 650),
76        (16.0, 220),
77        (27.0, 180),
78        (38.0, 200),
79        (45.0, 450),
80        (56.0, 200),
81        (67.0, 220),
82        (78.0, 260),
83        (87.0, 320),
84        (93.0, 480),
85        (97.0, 520),
86        (100.0, 300),
87    ];
88
89    let style = if rainbow {
90        Style::rainbow()
91    } else {
92        Style::default()
93    };
94    let mut builder = Loader::builder().total(total).style(style);
95    if geodesic {
96        builder = builder.ordering(Geodesic::default());
97    }
98    if let Some(path) = &art_path {
99        let text = std::fs::read_to_string(path).unwrap_or_else(|e| {
100            eprintln!("could not read {path}: {e}");
101            std::process::exit(1);
102        });
103        builder = builder.art(inkling::Art::parse(&text));
104    }
105    let loader = builder.start();
106    for &(percent, dwell) in schedule {
107        let done = (percent / 100.0 * total as f64) as u64;
108        loader.set(done);
109        loader.set_message(format!(
110            "dragon.iso   {:.1} / {:.1} MB",
111            done as f64 / mb,
112            total as f64 / mb
113        ));
114        pause(dwell);
115    }
116    loader.finish();
117
118    // A finish that ties it together.
119    let _ = execute!(io::stdout(), Print("\r\n"));
120    step(
121        "\u{2713}",
122        Color::Green,
123        "Verified checksum  sha256:a1b2c3d4e5",
124    );
125    pause(400);
126    let _ = execute!(
127        io::stdout(),
128        Print("\r\n  "),
129        SetForegroundColor(Color::Green),
130        Print("Done"),
131        ResetColor,
132        Print(format!(
133            " in {:.1}s. dragon.iso (48.0 MB) ready.\r\n\r\n",
134            started.elapsed().as_secs_f64()
135        ))
136    );
137    let _ = io::stdout().flush();
138}