web_dev_server/
cli.rs

1use std::borrow::Cow;
2
3use owo_colors::OwoColorize;
4use tokio::task;
5
6use crate::{config::DevServerConfig, startup::Application};
7
8enum ValueTone {
9    Primary,
10    Success,
11    Warning,
12    Danger,
13    Accent,
14    Muted,
15}
16
17pub fn print_startup_summary(config: &DevServerConfig, app: &Application) {
18    let title = "WEB DEV SERVER";
19    let border = "=".repeat(title.len() + 8);
20
21    println!("{}", border.clone().bright_black());
22    println!("  {}", title.cyan().bold());
23    println!("{}", border.bright_black());
24
25    let address_primary = app.primary_url();
26    let address_alt = format!("http://localhost:{}", app.port());
27    let base_dir = Cow::Owned(app.base_dir().display().to_string());
28    let diff_mode = if app.diff_mode() {
29        Cow::Borrowed("ENABLED")
30    } else {
31        Cow::Borrowed("disabled")
32    };
33    let watching = if config.diff_mode {
34        Cow::Borrowed("Diff HTML/CSS updates")
35    } else {
36        Cow::Borrowed("Full page reloads")
37    };
38    let browser = if config.no_open_browser {
39        Cow::Borrowed("Manual (--no-open-browser)")
40    } else {
41        Cow::Borrowed("Auto-open on start")
42    };
43
44    let rows: Vec<(&str, Cow<'_, str>, ValueTone)> = vec![
45        ("Address", Cow::Owned(address_primary), ValueTone::Primary),
46        ("Alt", Cow::Owned(address_alt), ValueTone::Muted),
47        ("Base Dir", base_dir, ValueTone::Accent),
48        (
49            "Diff Mode",
50            diff_mode,
51            if app.diff_mode() {
52                ValueTone::Success
53            } else {
54                ValueTone::Danger
55            },
56        ),
57        ("Watching", watching, ValueTone::Warning),
58        ("Browser", browser, ValueTone::Accent),
59        (
60            "Exit",
61            Cow::Borrowed("Press Ctrl+C to stop"),
62            ValueTone::Accent,
63        ),
64    ];
65
66    let label_width = rows
67        .iter()
68        .map(|(label, _, _)| label.len())
69        .max()
70        .unwrap_or(0)
71        + 1;
72
73    for (label, value, tone) in rows {
74        let padded_label = format!("{label:<label_width$}:", label_width = label_width);
75        let colored_label = format!("{}", padded_label.bright_blue().bold());
76        let colored_value = colorize(value.as_ref(), tone);
77
78        println!("  {} {}", colored_label, colored_value);
79    }
80
81    println!();
82    println!(
83        "  {} {}",
84        "Serving".bright_black(),
85        app.base_dir().display().to_string().bright_black()
86    );
87    if config.no_open_browser {
88        println!(
89            "  {}",
90            "Browser launch disabled (--no-open-browser)."
91                .bright_black()
92                .italic()
93        );
94    } else {
95        println!(
96            "  {}",
97            "Copy the address above if your browser did not open automatically."
98                .bright_black()
99                .italic()
100        );
101    }
102    println!(
103        "  {}",
104        "Leave this terminal open to keep the live server running."
105            .bright_black()
106            .italic()
107    );
108    println!();
109}
110
111fn colorize(value: &str, tone: ValueTone) -> String {
112    match tone {
113        ValueTone::Primary => value.bold().bright_white().to_string(),
114        ValueTone::Success => value.bold().bright_green().to_string(),
115        ValueTone::Warning => value.to_string().bright_yellow().to_string(),
116        ValueTone::Danger => value.bold().bright_red().to_string(),
117        ValueTone::Accent => value.to_string().bright_cyan().to_string(),
118        ValueTone::Muted => value.to_string().dimmed().to_string(),
119    }
120}
121
122pub fn launch_browser(url: String) {
123    task::spawn(async move {
124        match task::spawn_blocking(move || open::that(url)).await {
125            Ok(Ok(())) => {}
126            Ok(Err(error)) => {
127                eprintln!("[web-dev-server] failed to open browser: {error}");
128            }
129            Err(error) => {
130                eprintln!("[web-dev-server] browser task join error: {error}");
131            }
132        }
133    });
134}