html terminal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use super::{get_nextblock, Context, FAIL, SUCCESS};
use crate::send_ctx;
use anyhow::Result;
use itertools::Itertools;
use std::str::FromStr;

fn parse(line: &str) -> Option<(u32, u32, u32)> {
    line.split('/')
        .map(|s| u32::from_str(s.trim().split_once(' ').unwrap().0).unwrap())
        .collect_tuple()
}

#[allow(dead_code)]
pub enum Size {
    Gb(f64),
    Mb(f64),
    Kb(f64),
    B(f64),
}
const UNIT: f64 = 1024.0;

impl Size {
    pub fn bytes(self) -> f64 {
        match self {
            Self::B(x) => x,
            Self::Kb(x) => x * UNIT,
            Self::Mb(x) => x * UNIT * UNIT,
            Self::Gb(x) => x * UNIT * UNIT * UNIT,
        }
    }
}
// https://git.sr.ht/~f9/human_bytes
pub fn humanize_bytes<T: Into<Size>>(bytes: T) -> String {
    const SUFFIX: [&str; 4] = ["B", "KB", "MB", "GB"];
    let size = dbg!(bytes.into().bytes());

    if size <= 0.0 {
        return "0 B".to_string();
    }

    let base = size.log10() / UNIT.log10();

    let result = format!("{:.1}", UNIT.powf(base - base.floor()),)
        .trim_end_matches(".0")
        .to_owned();

    // Add suffix
    [&result, SUFFIX[base.floor() as usize]].join(" ")
}

#[poise::command(prefix_command, slash_command, category = "Info", rename = "status")]
/// server status.
pub async fn command(ctx: Context<'_>) -> Result<()> {
    let _ = ctx.defer_or_broadcast().await;
    send_ctx!(ctx, "status")?;
    let block = tokio::select! {
        block = get_nextblock() => block,
        _ = async_std::task::sleep(std::time::Duration::from_secs(5)) =>
            { poise::send_reply(ctx, |m| m.embed(|e| e.title("server down").color(FAIL))).await?; return Ok(()) },
    };
    let (tps, mem, pcount) =
        parse(&block).ok_or(anyhow::anyhow!("couldnt split block {block}."))?;
    poise::send_reply(ctx, |m| {
        m.embed(|e| {
            if pcount > 0 {
                e.footer(|f| f.text("see /players for player list"));
            }
            e.title("server online")
                .field("tps", tps, true)
                .field("memory use", humanize_bytes(Size::Mb(mem as f64)), true)
                .field("players", pcount, true)
                .color(SUCCESS)
        })
    })
    .await?;
    Ok(())
}

#[test]
fn test_parse() {
    assert!(parse("57 TPS / 274 MB / 7 PLAYERS") == Some((57, 274, 7)));
}

#[test]
fn test_bytes() {
    assert!(humanize_bytes(Size::B(0.0)) == "0 B");
    assert!(humanize_bytes(Size::B(550.0)) == "550 B");
    assert!(humanize_bytes(Size::Kb(550.0)) == "550 KB");
    assert!(humanize_bytes(Size::Mb(650.0)) == "650 MB");
    assert!(humanize_bytes(Size::Gb(15.3)) == "15.3 GB");
}